| /* |
| * 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); |
| } |
| } |