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.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