Home | History | Annotate | Download | only in subscriber
      1 /*
      2  * Copyright (C) 2019 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 package android.cts.statsd.subscriber;
     17 
     18 import com.android.compatibility.common.util.CpuFeatures;
     19 import com.android.internal.os.StatsdConfigProto;
     20 import com.android.os.AtomsProto;
     21 import com.android.os.ShellConfig;
     22 import com.android.os.statsd.ShellDataProto;
     23 import com.android.tradefed.device.CollectingByteOutputReceiver;
     24 import com.android.tradefed.device.DeviceNotAvailableException;
     25 import com.android.tradefed.device.ITestDevice;
     26 import com.android.tradefed.log.LogUtil;
     27 import com.android.tradefed.testtype.DeviceTestCase;
     28 import com.google.common.io.Files;
     29 import com.google.protobuf.InvalidProtocolBufferException;
     30 
     31 import java.io.File;
     32 import java.nio.ByteBuffer;
     33 import java.nio.ByteOrder;
     34 import java.util.Arrays;
     35 
     36 /**
     37  * Statsd shell data subscription test.
     38  */
     39 public class ShellSubscriberTest extends DeviceTestCase {
     40     private int sizetBytes;
     41 
     42     @Override
     43     protected void setUp() throws Exception {
     44         super.setUp();
     45         sizetBytes = getSizetBytes();
     46     }
     47 
     48     private int getSizetBytes() {
     49         try {
     50             ITestDevice device = getDevice();
     51             if (CpuFeatures.isArm64(device)) {
     52                 return 8;
     53             }
     54             if (CpuFeatures.isArm32(device)) {
     55                 return 4;
     56             }
     57             return -1;
     58         } catch (DeviceNotAvailableException e) {
     59             return -1;
     60         }
     61     }
     62 
     63     // Tests that anomaly detection for count works.
     64     // Also tests that anomaly detection works when spanning multiple buckets.
     65     public void testShellSubscription() {
     66         if (sizetBytes < 0) {
     67             return;
     68         }
     69         // choose a pulled atom that is likely to be supported on all devices (SYSTEM_UPTIME).
     70         // Testing pushed atom is a little trickier, because the executeShellCommand() is blocking
     71         // and we cannot push a breadcrumb event at the same time when the shell subscription is
     72         // running. So test pulled atom instead.
     73         ShellConfig.ShellSubscription config = ShellConfig.ShellSubscription.newBuilder()
     74                 .addPulled(ShellConfig.PulledAtomSubscription.newBuilder().setMatcher(
     75                         StatsdConfigProto.SimpleAtomMatcher.newBuilder()
     76                                 .setAtomId(AtomsProto.Atom.SYSTEM_UPTIME_FIELD_NUMBER).build())
     77                         .setFreqMillis(2000).build()).build();
     78         CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
     79         startSubscription(config, receiver, 10);
     80         byte[] output = receiver.getOutput();
     81         // There should be at lease some data returned.
     82         assertTrue(output.length > sizetBytes);
     83 
     84         int atomCount = 0;
     85         int i = 0;
     86         while (output.length > i + sizetBytes) {
     87             int len = 0;
     88             for (int j = 0; j < sizetBytes; j++) {
     89                 len += ((int) output[i + j] & 0xffL) << (sizetBytes * j);
     90             }
     91             LogUtil.CLog.d("received : " + output.length + " bytes, size : " + len);
     92 
     93             if (output.length < i + sizetBytes + len) {
     94                 fail("Bad data received.");
     95             }
     96 
     97             try {
     98                 ShellDataProto.ShellData data =
     99                         ShellDataProto.ShellData.parseFrom(
    100                                 Arrays.copyOfRange(output, i + sizetBytes, i + sizetBytes + len));
    101                 assertTrue(data.getAtomCount() > 0);
    102                 assertTrue(data.getAtom(0).hasSystemUptime());
    103                 atomCount++;
    104                 LogUtil.CLog.d("Received " + data.toString());
    105             } catch (InvalidProtocolBufferException e) {
    106                 fail("Failed to parse proto");
    107             }
    108             i += (sizetBytes + len);
    109         }
    110 
    111         assertTrue(atomCount > 0);
    112     }
    113 
    114     private void startSubscription(ShellConfig.ShellSubscription config,
    115                                    CollectingByteOutputReceiver receiver, int waitTimeSec) {
    116         LogUtil.CLog.d("Uploading the following config:\n" + config.toString());
    117         try {
    118             File configFile = File.createTempFile("shellconfig", ".config");
    119             configFile.deleteOnExit();
    120             int length = config.toByteArray().length;
    121             byte[] combined = new byte[sizetBytes + config.toByteArray().length];
    122 
    123             System.arraycopy(IntToByteArrayLittleEndian(length), 0, combined, 0, sizetBytes);
    124             System.arraycopy(config.toByteArray(), 0, combined, sizetBytes, length);
    125 
    126             Files.write(combined, configFile);
    127             String remotePath = "/data/local/tmp/" + configFile.getName();
    128             getDevice().pushFile(configFile, remotePath);
    129             LogUtil.CLog.d("waiting....................");
    130 
    131             getDevice().executeShellCommand(
    132                     String.join(" ", "cat", remotePath, "|", "cmd stats data-subscribe ",
    133                             String.valueOf(waitTimeSec)), receiver);
    134             getDevice().executeShellCommand("rm " + remotePath);
    135         } catch (Exception e) {
    136             fail(e.getMessage());
    137         }
    138     }
    139 
    140     byte[] IntToByteArrayLittleEndian(int length) {
    141         ByteBuffer b = ByteBuffer.allocate(sizetBytes);
    142         b.order(ByteOrder.LITTLE_ENDIAN);
    143         b.putInt(length);
    144         return b.array();
    145     }
    146 }
    147