blob: 78e360bdb83132c00ab223031a734f5f747a8393 [file] [log] [blame]
/*
* Copyright (C) 2011 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.
*/
package com.android.frameworkperf;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Spinner;
import android.widget.TextView;
import java.util.ArrayList;
/**
* So you thought sync used up your battery life.
*/
public class FrameworkPerfActivity extends Activity
implements AdapterView.OnItemSelectedListener {
static final String TAG = "Perf";
static final boolean DEBUG = false;
Spinner mFgSpinner;
Spinner mBgSpinner;
Spinner mLimitSpinner;
TextView mLimitLabel;
TextView mTestTime;
Button mStartButton;
Button mStopButton;
CheckBox mLocalCheckBox;
TextView mLog;
PowerManager.WakeLock mPartialWakeLock;
long mMaxRunTime = 5000;
boolean mLimitIsIterations;
boolean mStarted;
final String[] mAvailOpLabels;
final String[] mAvailOpDescriptions;
final String[] mLimitLabels = { "Time", "Iterations" };
int mFgTestIndex = -1;
int mBgTestIndex = -1;
TestService.Op mFgTest;
TestService.Op mBgTest;
int mCurOpIndex = 0;
TestConnection mCurConnection;
boolean mConnectionBound;
final ArrayList<RunResult> mResults = new ArrayList<RunResult>();
Object mResultNotifier = new Object();
class TestConnection implements ServiceConnection, IBinder.DeathRecipient {
Messenger mService;
boolean mLinked;
@Override public void onServiceConnected(ComponentName name, IBinder service) {
try {
if (!(service instanceof Binder)) {
// If remote, we'll be killing ye.
service.linkToDeath(this, 0);
mLinked = true;
}
mService = new Messenger(service);
dispatchCurOp(this);
} catch (RemoteException e) {
// Whoops, service has disappeared... try starting again.
Log.w(TAG, "Test service died, starting again");
startCurOp();
}
}
@Override public void onServiceDisconnected(ComponentName name) {
}
@Override public void binderDied() {
cleanup();
connectionDied(this);
}
void cleanup() {
if (mLinked) {
mLinked = false;
mService.getBinder().unlinkToDeath(this, 0);
}
}
}
static final int MSG_DO_NEXT_TEST = 1000;
final Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case TestService.RES_TEST_FINISHED: {
Bundle bundle = (Bundle)msg.obj;
bundle.setClassLoader(getClassLoader());
RunResult res = (RunResult)bundle.getParcelable("res");
completeCurOp(res);
} break;
case MSG_DO_NEXT_TEST: {
startCurOp();
} break;
}
}
};
final Messenger mMessenger = new Messenger(mHandler);
public FrameworkPerfActivity() {
mAvailOpLabels = new String[TestService.mAvailOps.length];
mAvailOpDescriptions = new String[TestService.mAvailOps.length];
for (int i=0; i<TestService.mAvailOps.length; i++) {
TestService.Op op = TestService.mAvailOps[i];
if (op == null) {
mAvailOpLabels[i] = "All";
mAvailOpDescriptions[i] = "All tests";
} else {
mAvailOpLabels[i] = op.getName();
if (mAvailOpLabels[i] == null) {
mAvailOpLabels[i] = "Nothing";
}
mAvailOpDescriptions[i] = op.getLongName();
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the layout for this activity. You can find it
// in res/layout/hello_activity.xml
setContentView(R.layout.main);
mFgSpinner = (Spinner) findViewById(R.id.fgspinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, mAvailOpLabels);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mFgSpinner.setAdapter(adapter);
mFgSpinner.setOnItemSelectedListener(this);
mBgSpinner = (Spinner) findViewById(R.id.bgspinner);
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, mAvailOpLabels);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mBgSpinner.setAdapter(adapter);
mBgSpinner.setOnItemSelectedListener(this);
mLimitSpinner = (Spinner) findViewById(R.id.limitspinner);
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, mLimitLabels);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mLimitSpinner.setAdapter(adapter);
mLimitSpinner.setOnItemSelectedListener(this);
mTestTime = (TextView)findViewById(R.id.testtime);
mLimitLabel = (TextView)findViewById(R.id.limitlabel);
mStartButton = (Button)findViewById(R.id.start);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
startRunning();
}
});
mStopButton = (Button)findViewById(R.id.stop);
mStopButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
stopRunning();
}
});
mStopButton.setEnabled(false);
mLocalCheckBox = (CheckBox)findViewById(R.id.local);
mLog = (TextView)findViewById(R.id.log);
mLog.setTextColor(Color.RED);
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Scheduler");
mPartialWakeLock.setReferenceCounted(false);
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (parent == mFgSpinner || parent == mBgSpinner || parent == mLimitSpinner) {
TestService.Op op = TestService.mAvailOps[position];
if (parent == mFgSpinner) {
mFgTestIndex = position;
mFgTest = op;
((TextView)findViewById(R.id.fgtext)).setText(mAvailOpDescriptions[position]);
} else if (parent == mBgSpinner) {
mBgTestIndex = position;
mBgTest = op;
((TextView)findViewById(R.id.bgtext)).setText(mAvailOpDescriptions[position]);
} else if (parent == mLimitSpinner) {
mLimitIsIterations = (position != 0);
if (mLimitIsIterations) {
mLimitLabel.setText("Iterations: ");
} else {
mLimitLabel.setText("Test time (ms): ");
}
}
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onDestroy() {
super.onDestroy();
stopRunning();
if (mPartialWakeLock.isHeld()) {
mPartialWakeLock.release();
}
}
void dispatchCurOp(TestConnection conn) {
if (mCurConnection != conn) {
Log.w(TAG, "Dispatching on invalid connection: " + conn);
return;
}
TestArgs args = new TestArgs();
if (mLimitIsIterations) {
args.maxOps = mMaxRunTime;
} else {
args.maxTime = mMaxRunTime;
}
if (mFgTestIndex == 0 && mBgTestIndex == 0) {
args.combOp = mCurOpIndex;
} else if (mFgTestIndex != 0 && mBgTestIndex != 0) {
args.fgOp = mFgTestIndex;
args.bgOp = mBgTestIndex;
} else {
// Skip null test.
if (mCurOpIndex == 0) {
mCurOpIndex = 1;
}
if (mFgTestIndex != 0) {
args.fgOp = mFgTestIndex;
args.bgOp = mCurOpIndex;
} else {
args.fgOp = mCurOpIndex;
args.bgOp = mBgTestIndex;
}
}
Bundle bundle = new Bundle();
bundle.putParcelable("args", args);
Message msg = Message.obtain(null, TestService.CMD_START_TEST, bundle);
msg.replyTo = mMessenger;
try {
conn.mService.send(msg);
} catch (RemoteException e) {
Log.w(TAG, "Failure communicating with service", e);
}
}
void completeCurOp(RunResult result) {
log(String.format("%s: fg=%d*%gms/op (%dms) / bg=%d*%gms/op (%dms)",
result.name, result.fgOps, result.getFgMsPerOp(), result.fgTime,
result.bgOps, result.getBgMsPerOp(), result.bgTime));
synchronized (mResults) {
mResults.add(result);
}
if (!mStarted) {
log("Stop");
stopRunning();
return;
}
if (mFgTest != null && mBgTest != null) {
log("Finished");
stopRunning();
return;
}
if (mFgTest == null && mBgTest == null) {
mCurOpIndex+=2;
if (mCurOpIndex >= TestService.mOpPairs.length) {
log("Finished");
stopRunning();
return;
}
} else {
mCurOpIndex++;
if (mCurOpIndex >= TestService.mAvailOps.length) {
log("Finished");
stopRunning();
return;
}
}
startCurOp();
}
void disconnect() {
final TestConnection conn = mCurConnection;
if (conn != null) {
if (DEBUG) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Log.i(TAG, "Unbinding " + conn, here);
}
if (mConnectionBound) {
unbindService(conn);
mConnectionBound = false;
}
if (conn.mLinked) {
Message msg = Message.obtain(null, TestService.CMD_TERMINATE);
try {
conn.mService.send(msg);
return;
} catch (RemoteException e) {
Log.w(TAG, "Test service aleady died when terminating");
}
}
conn.cleanup();
}
connectionDied(conn);
}
void connectionDied(TestConnection conn) {
if (mCurConnection == conn) {
// Now that we know the test process has died, we can commence
// the next test. Just give a little delay to allow the activity
// manager to know it has died as well (not a disaster if it hasn't
// yet, though).
if (mConnectionBound) {
unbindService(conn);
}
mCurConnection = null;
mHandler.sendMessageDelayed(Message.obtain(null, MSG_DO_NEXT_TEST), 100);
}
}
void startCurOp() {
if (DEBUG) Log.i(TAG, "startCurOp: mCurConnection=" + mCurConnection);
if (mCurConnection != null) {
disconnect();
return;
}
if (mStarted) {
mHandler.removeMessages(TestService.RES_TEST_FINISHED);
mHandler.removeMessages(TestService.RES_TERMINATED);
mHandler.removeMessages(MSG_DO_NEXT_TEST);
mCurConnection = new TestConnection();
Intent intent;
if (mLocalCheckBox.isChecked()) {
intent = new Intent(this, LocalTestService.class);
} else {
intent = new Intent(this, TestService.class);
}
if (DEBUG) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Log.i(TAG, "Binding " + mCurConnection, here);
}
bindService(intent, mCurConnection, BIND_AUTO_CREATE|BIND_IMPORTANT);
mConnectionBound = true;
}
}
void startRunning() {
if (!mStarted) {
log("Start");
mStarted = true;
mStartButton.setEnabled(false);
mStopButton.setEnabled(true);
mLocalCheckBox.setEnabled(false);
mTestTime.setEnabled(false);
mFgSpinner.setEnabled(false);
mBgSpinner.setEnabled(false);
mLimitSpinner.setEnabled(false);
updateWakeLock();
startService(new Intent(this, SchedulerService.class));
mCurOpIndex = 0;
mMaxRunTime = Integer.parseInt(mTestTime.getText().toString());
synchronized (mResults) {
mResults.clear();
}
startCurOp();
}
}
void stopRunning() {
if (mStarted) {
disconnect();
mStarted = false;
mStartButton.setEnabled(true);
mStopButton.setEnabled(false);
mLocalCheckBox.setEnabled(true);
mTestTime.setEnabled(true);
mFgSpinner.setEnabled(true);
mBgSpinner.setEnabled(true);
mLimitSpinner.setEnabled(true);
updateWakeLock();
stopService(new Intent(this, SchedulerService.class));
synchronized (mResults) {
Log.i("PerfRes", "\tTEST\tFgOps\tFgMsPerOp\tFgTime\tFgName\tBgOps\tBgMsPerOp\t"
+ "BgTime\tBgName");
for (int i=0; i<mResults.size(); i++) {
RunResult result = mResults.get(i);
float fgMsPerOp = result.getFgMsPerOp();
float bgMsPerOp = result.getBgMsPerOp();
String fgMsPerOpStr = fgMsPerOp != 0 ? Float.toString(fgMsPerOp) : "";
String bgMsPerOpStr = bgMsPerOp != 0 ? Float.toString(bgMsPerOp) : "";
Log.i("PerfRes", "\t" + result.name + "\t" + result.fgOps
+ "\t" + result.getFgMsPerOp() + "\t" + result.fgTime
+ "\t" + result.fgLongName + "\t" + result.bgOps
+ "\t" + result.getBgMsPerOp() + "\t" + result.bgTime
+ "\t" + result.bgLongName);
}
}
synchronized (mResultNotifier) {
mResultNotifier.notifyAll();
}
}
}
void updateWakeLock() {
if (mStarted) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (!mPartialWakeLock.isHeld()) {
mPartialWakeLock.acquire();
}
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
if (mPartialWakeLock.isHeld()) {
mPartialWakeLock.release();
}
}
}
void log(String s) {
mLog.setText(mLog.getText() + "\n" + s);
Log.i(TAG, s);
}
}