Home | History | Annotate | Download | only in frameworkperf
      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