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