FrameworkPerfActivity.java revision 5eefd7711f63bc1b67ec927a8c43363f426121cc
105ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root/*
205ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root * Copyright (C) 2011 The Android Open Source Project
305ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root *
405ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root * Licensed under the Apache License, Version 2.0 (the "License");
505ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root * you may not use this file except in compliance with the License.
605ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root * You may obtain a copy of the License at
705ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root *
805ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root *      http://www.apache.org/licenses/LICENSE-2.0
905ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root *
1005ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root * Unless required by applicable law or agreed to in writing, software
1105ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root * distributed under the License is distributed on an "AS IS" BASIS,
1205ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1305ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root * See the License for the specific language governing permissions and
1405ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root * limitations under the License.
1505ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root */
1605ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root
1705ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootpackage com.android.frameworkperf;
1805ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Root
1905ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootimport android.app.Activity;
2005ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootimport android.content.ComponentName;
2105ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootimport android.content.Intent;
2205ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootimport android.content.ServiceConnection;
2305ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootimport android.os.Binder;
2405ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootimport android.os.Bundle;
2505ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootimport android.os.Handler;
2605ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootimport android.os.IBinder;
2705ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootimport android.os.Message;
2805ca4c90644921df9193d92b2abdc81ef77e4a62Kenny Rootimport android.os.Messenger;
29import android.os.PowerManager;
30import android.os.RemoteException;
31import android.util.Log;
32import android.view.View;
33import android.view.WindowManager;
34import android.widget.AdapterView;
35import android.widget.ArrayAdapter;
36import android.widget.Button;
37import android.widget.CheckBox;
38import android.widget.Spinner;
39import android.widget.TextView;
40
41import java.util.ArrayList;
42
43/**
44 * So you thought sync used up your battery life.
45 */
46public class FrameworkPerfActivity extends Activity
47        implements AdapterView.OnItemSelectedListener {
48    static final String TAG = "Perf";
49    static final boolean DEBUG = false;
50
51    Spinner mFgSpinner;
52    Spinner mBgSpinner;
53    TextView mTestTime;
54    Button mStartButton;
55    Button mStopButton;
56    CheckBox mLocalCheckBox;
57    TextView mLog;
58    PowerManager.WakeLock mPartialWakeLock;
59
60    long mMaxRunTime = 5000;
61    boolean mStarted;
62
63    final String[] mAvailOpLabels;
64    final String[] mAvailOpDescriptions;
65
66    int mFgTestIndex = -1;
67    int mBgTestIndex = -1;
68    TestService.Op mFgTest;
69    TestService.Op mBgTest;
70    int mCurOpIndex = 0;
71    TestConnection mCurConnection;
72    boolean mConnectionBound;
73
74    final ArrayList<RunResult> mResults = new ArrayList<RunResult>();
75
76    Object mResultNotifier = new Object();
77
78    class TestConnection implements ServiceConnection, IBinder.DeathRecipient {
79        Messenger mService;
80        boolean mLinked;
81
82        @Override public void onServiceConnected(ComponentName name, IBinder service) {
83            try {
84                if (!(service instanceof Binder)) {
85                    // If remote, we'll be killing ye.
86                    service.linkToDeath(this, 0);
87                    mLinked = true;
88                }
89                mService = new Messenger(service);
90                dispatchCurOp(this);
91            } catch (RemoteException e) {
92                // Whoops, service has disappeared...  try starting again.
93                Log.w(TAG, "Test service died, starting again");
94                startCurOp();
95            }
96        }
97
98        @Override public void onServiceDisconnected(ComponentName name) {
99        }
100
101        @Override public void binderDied() {
102            cleanup();
103            connectionDied(this);
104        }
105
106        void cleanup() {
107            if (mLinked) {
108                mLinked = false;
109                mService.getBinder().unlinkToDeath(this, 0);
110            }
111        }
112    }
113
114    static final int MSG_DO_NEXT_TEST = 1000;
115
116    final Handler mHandler = new Handler() {
117        @Override public void handleMessage(Message msg) {
118            switch (msg.what) {
119                case TestService.RES_TEST_FINISHED: {
120                    Bundle bundle = (Bundle)msg.obj;
121                    bundle.setClassLoader(getClassLoader());
122                    RunResult res = (RunResult)bundle.getParcelable("res");
123                    completeCurOp(res);
124                } break;
125                case MSG_DO_NEXT_TEST: {
126                    startCurOp();
127                } break;
128            }
129        }
130    };
131
132    final Messenger mMessenger = new Messenger(mHandler);
133
134    public FrameworkPerfActivity() {
135        mAvailOpLabels = new String[TestService.mAvailOps.length];
136        mAvailOpDescriptions = new String[TestService.mAvailOps.length];
137        for (int i=0; i<TestService.mAvailOps.length; i++) {
138            TestService.Op op = TestService.mAvailOps[i];
139            if (op == null) {
140                mAvailOpLabels[i] = "All";
141                mAvailOpDescriptions[i] = "All tests";
142            } else {
143                mAvailOpLabels[i] = op.getName();
144                if (mAvailOpLabels[i] == null) {
145                    mAvailOpLabels[i] = "Nothing";
146                }
147                mAvailOpDescriptions[i] = op.getLongName();
148            }
149        }
150    }
151
152    @Override
153    public void onCreate(Bundle savedInstanceState) {
154        super.onCreate(savedInstanceState);
155
156        // Set the layout for this activity.  You can find it
157        // in res/layout/hello_activity.xml
158        setContentView(R.layout.main);
159
160        mFgSpinner = (Spinner) findViewById(R.id.fgspinner);
161        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
162                android.R.layout.simple_spinner_item, mAvailOpLabels);
163        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
164        mFgSpinner.setAdapter(adapter);
165        mFgSpinner.setOnItemSelectedListener(this);
166        mBgSpinner = (Spinner) findViewById(R.id.bgspinner);
167        adapter = new ArrayAdapter<String>(this,
168                android.R.layout.simple_spinner_item, mAvailOpLabels);
169        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
170        mBgSpinner.setAdapter(adapter);
171        mBgSpinner.setOnItemSelectedListener(this);
172
173        mTestTime = (TextView)findViewById(R.id.testtime);
174
175        mStartButton = (Button)findViewById(R.id.start);
176        mStartButton.setOnClickListener(new View.OnClickListener() {
177            @Override public void onClick(View v) {
178                startRunning();
179            }
180        });
181        mStopButton = (Button)findViewById(R.id.stop);
182        mStopButton.setOnClickListener(new View.OnClickListener() {
183            @Override public void onClick(View v) {
184                stopRunning();
185            }
186        });
187        mStopButton.setEnabled(false);
188        mLocalCheckBox = (CheckBox)findViewById(R.id.local);
189
190        mLog = (TextView)findViewById(R.id.log);
191
192        PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
193        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Scheduler");
194        mPartialWakeLock.setReferenceCounted(false);
195    }
196
197    @Override
198    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
199        if (parent == mFgSpinner || parent == mBgSpinner) {
200            TestService.Op op = TestService.mAvailOps[position];
201            if (parent == mFgSpinner) {
202                mFgTestIndex = position;
203                mFgTest = op;
204                ((TextView)findViewById(R.id.fgtext)).setText(mAvailOpDescriptions[position]);
205            } else {
206                mBgTestIndex = position;
207                mBgTest = op;
208                ((TextView)findViewById(R.id.bgtext)).setText(mAvailOpDescriptions[position]);
209            }
210        }
211    }
212
213    @Override
214    public void onNothingSelected(AdapterView<?> parent) {
215    }
216
217    @Override
218    public void onResume() {
219        super.onResume();
220    }
221
222    @Override
223    public void onDestroy() {
224        super.onDestroy();
225        stopRunning();
226        if (mPartialWakeLock.isHeld()) {
227            mPartialWakeLock.release();
228        }
229    }
230
231    void dispatchCurOp(TestConnection conn) {
232        if (mCurConnection != conn) {
233            Log.w(TAG, "Dispatching on invalid connection: " + conn);
234            return;
235        }
236        TestArgs args = new TestArgs();
237        args.maxTime = mMaxRunTime;
238        if (mFgTestIndex == 0 && mBgTestIndex == 0) {
239            args.combOp = mCurOpIndex;
240        } else if (mFgTestIndex != 0 && mBgTestIndex != 0) {
241            args.fgOp = mFgTestIndex;
242            args.bgOp = mBgTestIndex;
243        } else {
244            // Skip null test.
245            if (mCurOpIndex == 0) {
246                mCurOpIndex = 1;
247            }
248            if (mFgTestIndex != 0) {
249                args.fgOp = mFgTestIndex;
250                args.bgOp = mCurOpIndex;
251            } else {
252                args.fgOp = mCurOpIndex;
253                args.bgOp = mFgTestIndex;
254            }
255        }
256        Bundle bundle = new Bundle();
257        bundle.putParcelable("args", args);
258        Message msg = Message.obtain(null, TestService.CMD_START_TEST, bundle);
259        msg.replyTo = mMessenger;
260        try {
261            conn.mService.send(msg);
262        } catch (RemoteException e) {
263            Log.w(TAG, "Failure communicating with service", e);
264        }
265    }
266
267    void completeCurOp(RunResult result) {
268        log(String.format("%s: fg=%d*%gms/op (%dms) / bg=%d*%gms/op (%dms)",
269                result.name, result.fgOps, result.getFgMsPerOp(), result.fgTime,
270                result.bgOps, result.getBgMsPerOp(), result.bgTime));
271        synchronized (mResults) {
272            mResults.add(result);
273        }
274        if (!mStarted) {
275            log("Stop");
276            stopRunning();
277            return;
278        }
279        if (mFgTest != null && mBgTest != null) {
280            log("Finished");
281            stopRunning();
282            return;
283        }
284        if (mFgTest == null && mBgTest == null) {
285            mCurOpIndex+=2;
286            if (mCurOpIndex >= TestService.mOpPairs.length) {
287                log("Finished");
288                stopRunning();
289                return;
290            }
291        } else {
292            mCurOpIndex++;
293            if (mCurOpIndex >= TestService.mAvailOps.length) {
294                log("Finished");
295                stopRunning();
296                return;
297            }
298        }
299        startCurOp();
300    }
301
302    void disconnect() {
303        final TestConnection conn = mCurConnection;
304        if (conn != null) {
305            if (DEBUG) {
306                RuntimeException here = new RuntimeException("here");
307                here.fillInStackTrace();
308                Log.i(TAG, "Unbinding " + conn, here);
309            }
310            if (mConnectionBound) {
311                unbindService(conn);
312                mConnectionBound = false;
313            }
314            if (conn.mLinked) {
315                Message msg = Message.obtain(null, TestService.CMD_TERMINATE);
316                try {
317                    conn.mService.send(msg);
318                    return;
319                } catch (RemoteException e) {
320                    Log.w(TAG, "Test service aleady died when terminating");
321                }
322            }
323            conn.cleanup();
324        }
325        connectionDied(conn);
326    }
327
328    void connectionDied(TestConnection conn) {
329        if (mCurConnection == conn) {
330            // Now that we know the test process has died, we can commence
331            // the next test.  Just give a little delay to allow the activity
332            // manager to know it has died as well (not a disaster if it hasn't
333            // yet, though).
334            if (mConnectionBound) {
335                unbindService(conn);
336            }
337            mCurConnection = null;
338            mHandler.sendMessageDelayed(Message.obtain(null, MSG_DO_NEXT_TEST), 100);
339        }
340    }
341
342    void startCurOp() {
343        if (DEBUG) Log.i(TAG, "startCurOp: mCurConnection=" + mCurConnection);
344        if (mCurConnection != null) {
345            disconnect();
346            return;
347        }
348        if (mStarted) {
349            mHandler.removeMessages(TestService.RES_TEST_FINISHED);
350            mHandler.removeMessages(TestService.RES_TERMINATED);
351            mHandler.removeMessages(MSG_DO_NEXT_TEST);
352            mCurConnection = new TestConnection();
353            Intent intent;
354            if (mLocalCheckBox.isChecked()) {
355                intent = new Intent(this, LocalTestService.class);
356            } else {
357                intent = new Intent(this, TestService.class);
358            }
359            if (DEBUG) {
360                RuntimeException here = new RuntimeException("here");
361                here.fillInStackTrace();
362                Log.i(TAG, "Binding " + mCurConnection, here);
363            }
364            bindService(intent, mCurConnection, BIND_AUTO_CREATE|BIND_IMPORTANT);
365            mConnectionBound = true;
366        }
367    }
368
369    void startRunning() {
370        if (!mStarted) {
371            log("Start");
372            mStarted = true;
373            mStartButton.setEnabled(false);
374            mStopButton.setEnabled(true);
375            mLocalCheckBox.setEnabled(false);
376            mTestTime.setEnabled(false);
377            mFgSpinner.setEnabled(false);
378            mBgSpinner.setEnabled(false);
379            updateWakeLock();
380            startService(new Intent(this, SchedulerService.class));
381            mCurOpIndex = 0;
382            mMaxRunTime = Integer.parseInt(mTestTime.getText().toString());
383            synchronized (mResults) {
384                mResults.clear();
385            }
386            startCurOp();
387        }
388    }
389
390    void stopRunning() {
391        if (mStarted) {
392            disconnect();
393            mStarted = false;
394            mStartButton.setEnabled(true);
395            mStopButton.setEnabled(false);
396            mLocalCheckBox.setEnabled(true);
397            mTestTime.setEnabled(true);
398            mFgSpinner.setEnabled(true);
399            mBgSpinner.setEnabled(true);
400            updateWakeLock();
401            stopService(new Intent(this, SchedulerService.class));
402            synchronized (mResults) {
403                for (int i=0; i<mResults.size(); i++) {
404                    RunResult result = mResults.get(i);
405                    float fgMsPerOp = result.getFgMsPerOp();
406                    float bgMsPerOp = result.getBgMsPerOp();
407                    String fgMsPerOpStr = fgMsPerOp != 0 ? Float.toString(fgMsPerOp) : "";
408                    String bgMsPerOpStr = bgMsPerOp != 0 ? Float.toString(bgMsPerOp) : "";
409                    Log.i("PerfRes", "\t" + result.name + "\t" + result.fgOps
410                            + "\t" + result.getFgMsPerOp() + "\t" + result.fgTime
411                            + "\t" + result.fgLongName + "\t" + result.bgOps
412                            + "\t" + result.getBgMsPerOp() + "\t" + result.bgTime
413                            + "\t" + result.bgLongName);
414                }
415            }
416            synchronized (mResultNotifier) {
417                mResultNotifier.notifyAll();
418            }
419        }
420    }
421
422    void updateWakeLock() {
423        if (mStarted) {
424            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
425            if (!mPartialWakeLock.isHeld()) {
426                mPartialWakeLock.acquire();
427            }
428        } else {
429            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
430            if (mPartialWakeLock.isHeld()) {
431                mPartialWakeLock.release();
432            }
433        }
434    }
435
436    void log(String s) {
437        mLog.setText(mLog.getText() + "\n" + s);
438        Log.i(TAG, s);
439    }
440}
441