1 /*
2  * Copyright (C) 2016 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 test.amslam;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.SystemClock;
23 import android.os.Bundle;
24 import android.text.method.ScrollingMovementMethod;
25 import android.util.Log;
26 import android.view.View;
27 import android.widget.TextView;
28 
29 import java.util.Queue;
30 import java.util.concurrent.ArrayBlockingQueue;
31 import java.util.concurrent.BlockingQueue;
32 import java.util.concurrent.ConcurrentLinkedQueue;
33 
34 public class MainActivity extends Activity implements PongReceiver.PingPongResponseListener {
35     private static final String TAG = "AmSlam";
36 
37     private static final Class<?>[] sTargets;
38     private static final BlockingQueue<Intent> sWorkQueue = new ArrayBlockingQueue<>(100);
39     private static Context sAppContext;
40     private static final int[] CONCURRENT_TESTS = {1, 2, 4, 6, 8, 10};
41 
42     private boolean mAutoRun;
43 
44     private TextView mOutput;
45 
46     private int mTestPhase;
47     private long mBatchStartTime;
48     private int mPendingResponses;
49 
50     private int mBatchRemaining;
51     private int mCurrentTargetIndex;
52 
53     private int mTotalReceived;
54     private long mTotalTime;
55     private long mTotalPingTime;
56     private long mTotalPongTime;
57 
58     @Override
onCreate(Bundle savedInstanceState)59     protected void onCreate(Bundle savedInstanceState) {
60         super.onCreate(savedInstanceState);
61         sAppContext = getApplicationContext();
62         setContentView(R.layout.activity_main);
63         mOutput = findViewById(R.id.output);
64         PongReceiver.addListener(this);
65 
66         findViewById(R.id.run).setOnClickListener(view -> {
67             view.setEnabled(false);
68             mOutput.setText("");
69             startBatch();
70         });
71 
72         mAutoRun = getIntent().getBooleanExtra("autorun", false);
73         if (mAutoRun) {
74             findViewById(R.id.run).performClick();
75         }
76     }
77 
78     @Override
onDestroy()79     protected void onDestroy() {
80         super.onDestroy();
81         PongReceiver.removeListener(this);
82     }
83 
startBatch()84     private void startBatch() {
85         if (mBatchRemaining > 0 || mPendingResponses > 0) {
86             // Still sending, skip
87             return;
88         }
89         mBatchStartTime = SystemClock.uptimeMillis();
90         mBatchRemaining = 10 * CONCURRENT_TESTS[mTestPhase];
91         mTotalReceived = 0;
92         mTotalTime = mTotalPingTime = mTotalPongTime = 0;
93         log("Starting test with " + CONCURRENT_TESTS[mTestPhase] + " concurrent requests...\n");
94         continueSend();
95     }
96 
nextTarget()97     private Class<?> nextTarget() {
98         Class<?> ret = sTargets[mCurrentTargetIndex];
99         mCurrentTargetIndex = (mCurrentTargetIndex + 1) % sTargets.length;
100         return ret;
101     }
102 
continueSend()103     private void continueSend() {
104         while (mPendingResponses < CONCURRENT_TESTS[mTestPhase] && mBatchRemaining > 0) {
105             mPendingResponses++;
106             mBatchRemaining--;
107             Class<?> target = nextTarget();
108             Intent intent = new Intent(getApplicationContext(), target);
109             try {
110                 sWorkQueue.put(intent);
111             } catch (InterruptedException e) {
112                 throw new RuntimeException(e);
113             }
114         }
115     }
116 
117     @Override
onPingPongResponse(long send, long bounce, long recv, String remote)118     public void onPingPongResponse(long send, long bounce, long recv, String remote) {
119         if (send < mBatchStartTime || mPendingResponses == 0) {
120             Log.e(TAG, "received outdated response??");
121             Log.e(TAG, "send " + send + ", bounce " + bounce + ", recv " + recv
122                     + ", batchStart " + mBatchStartTime + ", remote: " + remote);
123         }
124         mPendingResponses--;
125         mTotalReceived++;
126         continueSend();
127         mTotalTime += (recv - send);
128         mTotalPingTime += (bounce - send);
129         mTotalPongTime += (recv - bounce);
130         if (mPendingResponses == 0) {
131             long now = SystemClock.uptimeMillis();
132             log(String.format("Sent %d ping/pongs, %d concurrent.\n"
133                     + "Total duration %dms (%dms eff. avg)\n"
134                     + "Average message took %dms (%dms + %dms)\n",
135                     mTotalReceived, CONCURRENT_TESTS[mTestPhase],
136                     (now - mBatchStartTime), (now - mBatchStartTime) / mTotalReceived,
137                     mTotalTime / mTotalReceived, mTotalPingTime / mTotalReceived,
138                     mTotalPongTime / mTotalReceived));
139 
140             mTestPhase++;
141             if (mTestPhase < CONCURRENT_TESTS.length) {
142                 startBatch();
143             } else {
144                 mTestPhase = 0;
145                 log("Finished\n");
146                 findViewById(R.id.run).setEnabled(true);
147                 if (mAutoRun) {
148                     finish();
149                 }
150             }
151         }
152     }
153 
log(String text)154     private void log(String text) {
155         mOutput.append(text);
156         Log.d(TAG, text);
157     }
158 
159     static {
160         sTargets = new Class<?>[100];
161         for (int i = 0; i < sTargets.length; i++) {
162             try {
163                 sTargets[i] = Class.forName(
164                         String.format("test.amslam.subreceivers.PingReceiver%03d", i));
165             } catch (ClassNotFoundException e) {
166                 throw new RuntimeException(e);
167             }
168         }
169 
170         Runnable work = () -> {
171             while (true) {
172                 try {
173                     Intent intent = sWorkQueue.take();
174                     intent.putExtra("start_time", SystemClock.uptimeMillis());
175                     sAppContext.startService(intent);
176                 } catch (InterruptedException e) {}
177             }
178         };
179 
180         // How many worker threads should we spawn? ¯\_(ツ)_/¯
181         for (int i = 0; i < 10; i++) {
182             new Thread(work, "Slammer" + i).start();
183         }
184     }
185 }
186