1 /*
2  * Copyright (C) 2020 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.internal.os;
18 
19 import android.os.Binder;
20 
21 import com.android.internal.os.BinderCallHeavyHitterWatcher.HeavyHitterContainer;
22 
23 import junit.framework.TestCase;
24 
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.Random;
28 
29 /**
30  * Tests for {@link BinderCallHeavyHitterWatcher}.
31  */
32 public final class BinderHeavyHitterTest extends TestCase {
33 
34     private boolean mListenerNotified = false;
35 
36     private List<HeavyHitterContainer> mExpectedResult = null;
37 
38     /**
39      * Generate random input.
40      */
generateRandomInput(final int total, final List<HeavyHitterContainer> heavyHitters, final List<Integer> numOfHeavyHits)41     private ArrayList<HeavyHitterContainer> generateRandomInput(final int total,
42             final List<HeavyHitterContainer> heavyHitters,
43             final List<Integer> numOfHeavyHits) {
44         final ArrayList<HeavyHitterContainer> result = new ArrayList<>();
45         List<HeavyHitterContainer> flatternedHeavyHitters = null;
46         int totalHeavyHitters = 0;
47         if (numOfHeavyHits != null) {
48             flatternedHeavyHitters = new ArrayList<>();
49             for (int i = numOfHeavyHits.size() - 1; i >= 0; i--) {
50                 final int k = numOfHeavyHits.get(i);
51                 totalHeavyHitters += k;
52                 final HeavyHitterContainer container = heavyHitters.get(i);
53                 for (int j = 0; j < k; j++) {
54                     flatternedHeavyHitters.add(container);
55                 }
56             }
57         }
58         int totalLightHitters = total - totalHeavyHitters;
59         final Binder[] binders = {new TestBinder1(), new TestBinder2(), new TestBinder3()};
60         final int maxUid = 1000;
61         final int maxCode = 1000;
62         final Random rand = new Random();
63         for (int i = 0; i < total; i++) {
64             HeavyHitterContainer container = null;
65             if (totalLightHitters <= 0) {
66                 container = flatternedHeavyHitters.remove(rand.nextInt(totalHeavyHitters));
67                 totalHeavyHitters--;
68             } else if (totalHeavyHitters <= 0) {
69                 container = newContainer(rand.nextInt(maxUid),
70                         binders[rand.nextInt(binders.length)].getClass(),
71                         rand.nextInt(maxCode), 0.0f);
72                 totalLightHitters--;
73             } else {
74                 int val = rand.nextInt(total - i);
75                 if (val >= totalLightHitters) {
76                     container = flatternedHeavyHitters.remove(rand.nextInt(totalHeavyHitters));
77                     totalHeavyHitters--;
78                 } else {
79                     container = newContainer(rand.nextInt(maxUid),
80                             binders[rand.nextInt(binders.length)].getClass(),
81                             rand.nextInt(maxCode), 0.0f);
82                     totalLightHitters--;
83                 }
84             }
85             result.add(container);
86         }
87         return result;
88     }
89 
newContainer(final int uid, final Class clazz, final int code, final float freq)90     private HeavyHitterContainer newContainer(final int uid, final Class clazz, final int code,
91             final float freq) {
92         final HeavyHitterContainer container = new HeavyHitterContainer();
93         container.mUid = uid;
94         container.mClass = clazz;
95         container.mCode = code;
96         container.mFrequency = freq;
97         return container;
98     }
99 
onResult(final List<HeavyHitterContainer> results, final Integer inputSize, final Float threshod, final Long timeSpan)100     private void onResult(final List<HeavyHitterContainer> results, final Integer inputSize,
101             final Float threshod, final Long timeSpan) {
102         mListenerNotified = true;
103         if (mExpectedResult == null) {
104             assertTrue(results == null || results.size() == 0);
105         } else {
106             int size = mExpectedResult.size();
107             assertEquals(size, results.size());
108             for (int i = 0; i < size; i++) {
109                 final HeavyHitterContainer container = mExpectedResult.get(i);
110                 assertNotNull(container);
111                 assertTrue(results.remove(container));
112             }
113             assertEquals(0, results.size());
114         }
115     }
116 
testPositive()117     public void testPositive() throws Exception {
118         BinderCallHeavyHitterWatcher watcher = BinderCallHeavyHitterWatcher.getInstance();
119         try {
120             List<HeavyHitterContainer> hitters = new ArrayList<>();
121             List<Integer> counts = new ArrayList<>();
122             hitters.add(newContainer(1001, TestBinder4.class, 1002, 0.4f));
123             counts.add(400);
124             hitters.add(newContainer(2001, TestBinder5.class, 2002, 0.333f));
125             counts.add(333);
126             ArrayList<HeavyHitterContainer> inputs = generateRandomInput(1000, hitters, counts);
127             inputs.addAll((List<HeavyHitterContainer>) inputs.clone());
128 
129             watcher.setConfig(true, inputs.size(), 0.333f, this::onResult);
130             mListenerNotified = false;
131             mExpectedResult = hitters;
132 
133             for (int i = inputs.size() - 1; i >= 0; i--) {
134                 final HeavyHitterContainer container = inputs.get(i);
135                 watcher.onTransaction(container.mUid, container.mClass, container.mCode);
136             }
137             assertTrue(mListenerNotified);
138         } finally {
139             watcher.setConfig(false, 0, 0.0f, null);
140             mListenerNotified = false;
141             mExpectedResult = null;
142         }
143     }
144 
testNegative()145     public void testNegative() throws Exception {
146         BinderCallHeavyHitterWatcher watcher = BinderCallHeavyHitterWatcher.getInstance();
147         try {
148             List<HeavyHitterContainer> hitters = new ArrayList<>();
149             List<Integer> counts = new ArrayList<>();
150             hitters.add(newContainer(1001, TestBinder4.class, 1002, 0.332f));
151             counts.add(332);
152             hitters.add(newContainer(2001, TestBinder5.class, 2002, 0.331f));
153             counts.add(331);
154             ArrayList<HeavyHitterContainer> inputs = generateRandomInput(1000, hitters, counts);
155             inputs.addAll((List<HeavyHitterContainer>) inputs.clone());
156 
157             watcher.setConfig(true, inputs.size(), 0.333f, this::onResult);
158             mListenerNotified = false;
159             mExpectedResult = null;
160 
161             for (int i = inputs.size() - 1; i >= 0; i--) {
162                 final HeavyHitterContainer container = inputs.get(i);
163                 watcher.onTransaction(container.mUid, container.mClass, container.mCode);
164             }
165             assertFalse(mListenerNotified);
166         } finally {
167             watcher.setConfig(false, 0, 0.0f, null);
168             mListenerNotified = false;
169             mExpectedResult = null;
170         }
171     }
172 
173     private class TestBinder1 extends Binder {
174     }
175 
176     private class TestBinder2 extends Binder {
177     }
178 
179     private class TestBinder3 extends Binder {
180     }
181 
182     private class TestBinder4 extends Binder {
183     }
184 
185     private class TestBinder5 extends Binder {
186     }
187 }
188