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