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