1 /* 2 * Copyright (C) 2008 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.ide.eclipse.ddms.views; 18 19 import com.android.ddmlib.AndroidDebugBridge; 20 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; 21 import com.android.ddmlib.Client; 22 import com.android.ddmlib.ClientData; 23 import com.android.ddmlib.ClientData.IHprofDumpHandler; 24 import com.android.ddmlib.ClientData.MethodProfilingStatus; 25 import com.android.ddmlib.DdmPreferences; 26 import com.android.ddmlib.IDevice; 27 import com.android.ddmlib.SyncException; 28 import com.android.ddmlib.SyncService; 29 import com.android.ddmlib.SyncService.ISyncProgressMonitor; 30 import com.android.ddmlib.TimeoutException; 31 import com.android.ddmuilib.DevicePanel; 32 import com.android.ddmuilib.DevicePanel.IUiSelectionListener; 33 import com.android.ddmuilib.ImageLoader; 34 import com.android.ddmuilib.ScreenShotDialog; 35 import com.android.ddmuilib.SyncProgressHelper; 36 import com.android.ddmuilib.SyncProgressHelper.SyncRunnable; 37 import com.android.ddmuilib.handler.BaseFileHandler; 38 import com.android.ddmuilib.handler.MethodProfilingHandler; 39 import com.android.ide.eclipse.ddms.DdmsPlugin; 40 import com.android.ide.eclipse.ddms.IDebuggerConnector; 41 import com.android.ide.eclipse.ddms.i18n.Messages; 42 import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer; 43 44 import org.eclipse.core.filesystem.EFS; 45 import org.eclipse.core.filesystem.IFileStore; 46 import org.eclipse.core.resources.ResourcesPlugin; 47 import org.eclipse.core.runtime.IAdaptable; 48 import org.eclipse.core.runtime.Path; 49 import org.eclipse.jface.action.Action; 50 import org.eclipse.jface.action.IAction; 51 import org.eclipse.jface.action.IMenuManager; 52 import org.eclipse.jface.action.IToolBarManager; 53 import org.eclipse.jface.action.Separator; 54 import org.eclipse.jface.dialogs.MessageDialog; 55 import org.eclipse.jface.preference.IPreferenceStore; 56 import org.eclipse.jface.resource.ImageDescriptor; 57 import org.eclipse.swt.widgets.Composite; 58 import org.eclipse.swt.widgets.Display; 59 import org.eclipse.swt.widgets.Shell; 60 import org.eclipse.ui.IActionBars; 61 import org.eclipse.ui.ISharedImages; 62 import org.eclipse.ui.IWorkbench; 63 import org.eclipse.ui.IWorkbenchPage; 64 import org.eclipse.ui.IWorkbenchWindow; 65 import org.eclipse.ui.PartInitException; 66 import org.eclipse.ui.PlatformUI; 67 import org.eclipse.ui.WorkbenchException; 68 import org.eclipse.ui.ide.IDE; 69 import org.eclipse.ui.part.ViewPart; 70 71 import java.io.File; 72 import java.io.IOException; 73 74 public class DeviceView extends ViewPart implements IUiSelectionListener, IClientChangeListener { 75 76 private final static boolean USE_SELECTED_DEBUG_PORT = true; 77 78 public static final String ID = "com.android.ide.eclipse.ddms.views.DeviceView"; //$NON-NLS-1$ 79 80 private static DeviceView sThis; 81 82 private Shell mParentShell; 83 private DevicePanel mDeviceList; 84 85 private Action mResetAdbAction; 86 private Action mCaptureAction; 87 private Action mUpdateThreadAction; 88 private Action mUpdateHeapAction; 89 private Action mGcAction; 90 private Action mKillAppAction; 91 private Action mDebugAction; 92 private Action mHprofAction; 93 private Action mTracingAction; 94 95 private ImageDescriptor mTracingStartImage; 96 private ImageDescriptor mTracingStopImage; 97 98 public class HProfHandler extends BaseFileHandler implements IHprofDumpHandler { 99 public final static String ACTION_SAVE = "hprof.save"; //$NON-NLS-1$ 100 public final static String ACTION_OPEN = "hprof.open"; //$NON-NLS-1$ 101 102 public final static String DOT_HPROF = ".hprof"; //$NON-NLS-1$ 103 104 HProfHandler(Shell parentShell) { 105 super(parentShell); 106 } 107 108 @Override 109 protected String getDialogTitle() { 110 return Messages.DeviceView_HPROF_Error; 111 } 112 113 @Override 114 public void onEndFailure(final Client client, final String message) { 115 mParentShell.getDisplay().asyncExec(new Runnable() { 116 @Override 117 public void run() { 118 try { 119 displayErrorFromUiThread( 120 Messages.DeviceView_Unable_Create_HPROF_For_Application, 121 client.getClientData().getClientDescription(), 122 message != null ? message + "\n\n" : ""); //$NON-NLS-1$ //$NON-NLS-2$ 123 } finally { 124 // this will make sure the dump hprof button is 125 // re-enabled for the 126 // current selection. as the client is finished dumping 127 // an hprof file 128 doSelectionChanged(mDeviceList.getSelectedClient()); 129 } 130 } 131 }); 132 } 133 134 @Override 135 public void onSuccess(final String remoteFilePath, final Client client) { 136 mParentShell.getDisplay().asyncExec(new Runnable() { 137 @Override 138 public void run() { 139 final IDevice device = client.getDevice(); 140 try { 141 // get the sync service to pull the HPROF file 142 final SyncService sync = client.getDevice().getSyncService(); 143 if (sync != null) { 144 // get from the preference what action to take 145 IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore(); 146 String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION); 147 148 if (ACTION_OPEN.equals(value)) { 149 File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$ 150 final String tempPath = temp.getAbsolutePath(); 151 SyncProgressHelper.run(new SyncRunnable() { 152 153 @Override 154 public void run(ISyncProgressMonitor monitor) 155 throws SyncException, IOException, 156 TimeoutException { 157 sync.pullFile(remoteFilePath, tempPath, monitor); 158 } 159 160 @Override 161 public void close() { 162 sync.close(); 163 } 164 }, 165 String.format(Messages.DeviceView_Pulling_From_Device, 166 remoteFilePath), 167 mParentShell); 168 169 open(tempPath); 170 } else { 171 // default action is ACTION_SAVE 172 promptAndPull(sync, 173 client.getClientData().getClientDescription() + DOT_HPROF, 174 remoteFilePath, Messages.DeviceView_Save_HPROF_File); 175 176 } 177 } else { 178 displayErrorFromUiThread( 179 Messages.DeviceView_Unable_Download_HPROF_From_Device_One_Param_First_Message, 180 device.getSerialNumber()); 181 } 182 } catch (SyncException e) { 183 if (e.wasCanceled() == false) { 184 displayErrorFromUiThread( 185 Messages.DeviceView_Unable_Download_HPROF_From_Device_Two_Param, 186 device.getSerialNumber(), e.getMessage()); 187 } 188 } catch (Exception e) { 189 displayErrorFromUiThread( 190 Messages.DeviceView_Unable_Download_HPROF_From_Device_One_Param_Second_Message, 191 device.getSerialNumber()); 192 193 } finally { 194 // this will make sure the dump hprof button is 195 // re-enabled for the 196 // current selection. as the client is finished dumping 197 // an hprof file 198 doSelectionChanged(mDeviceList.getSelectedClient()); 199 } 200 } 201 }); 202 } 203 204 @Override 205 public void onSuccess(final byte[] data, final Client client) { 206 mParentShell.getDisplay().asyncExec(new Runnable() { 207 @Override 208 public void run() { 209 // get from the preference what action to take 210 IPreferenceStore store = DdmsPlugin.getDefault().getPreferenceStore(); 211 String value = store.getString(PreferenceInitializer.ATTR_HPROF_ACTION); 212 213 if (ACTION_OPEN.equals(value)) { 214 try { 215 // no need to give an extension since we're going to 216 // convert the 217 // file anyway after. 218 File tempFile = saveTempFile(data, null /* extension */); 219 open(tempFile.getAbsolutePath()); 220 } catch (Exception e) { 221 String errorMsg = e.getMessage(); 222 displayErrorFromUiThread( 223 Messages.DeviceView_Failed_To_Save_HPROF_Data, 224 errorMsg != null ? ":\n" + errorMsg : "."); //$NON-NLS-1$ //$NON-NLS-2$ 225 } 226 } else { 227 // default action is ACTION_SAVE 228 promptAndSave(client.getClientData().getClientDescription() + DOT_HPROF, 229 data, Messages.DeviceView_Save_HPROF_File); 230 } 231 } 232 }); 233 } 234 235 private void open(String path) throws IOException, InterruptedException, PartInitException { 236 // make a temp file to convert the hprof into something 237 // readable by normal tools 238 File temp = File.createTempFile("android", DOT_HPROF); //$NON-NLS-1$ 239 String tempPath = temp.getAbsolutePath(); 240 241 String[] command = new String[3]; 242 command[0] = DdmsPlugin.getHprofConverter(); 243 command[1] = path; 244 command[2] = tempPath; 245 246 Process p = Runtime.getRuntime().exec(command); 247 p.waitFor(); 248 249 IFileStore fileStore = EFS.getLocalFileSystem().getStore(new Path(tempPath)); 250 if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) { 251 // before we open the file in an editor window, we make sure the 252 // current 253 // workbench page has an editor area (typically the ddms 254 // perspective doesn't). 255 IWorkbench workbench = PlatformUI.getWorkbench(); 256 IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); 257 IWorkbenchPage page = window.getActivePage(); 258 if (page.isEditorAreaVisible() == false) { 259 IAdaptable input; 260 if (page != null) 261 input = page.getInput(); 262 else 263 input = ResourcesPlugin.getWorkspace().getRoot(); 264 try { 265 workbench.showPerspective("org.eclipse.debug.ui.DebugPerspective", //$NON-NLS-1$ 266 window, input); 267 } catch (WorkbenchException e) { 268 } 269 } 270 271 IDE.openEditorOnFileStore(page, fileStore); 272 } 273 } 274 } 275 276 public DeviceView() { 277 // the view is declared with allowMultiple="false" so we 278 // can safely do this. 279 sThis = this; 280 } 281 282 public static DeviceView getInstance() { 283 return sThis; 284 } 285 286 @Override 287 public void createPartControl(Composite parent) { 288 mParentShell = parent.getShell(); 289 290 ImageLoader loader = ImageLoader.getDdmUiLibLoader(); 291 292 mDeviceList = new DevicePanel(USE_SELECTED_DEBUG_PORT); 293 mDeviceList.createPanel(parent); 294 mDeviceList.addSelectionListener(this); 295 296 DdmsPlugin plugin = DdmsPlugin.getDefault(); 297 mDeviceList.addSelectionListener(plugin); 298 plugin.setListeningState(true); 299 300 mCaptureAction = new Action(Messages.DeviceView_Screen_Capture) { 301 @Override 302 public void run() { 303 ScreenShotDialog dlg = new ScreenShotDialog( 304 DdmsPlugin.getDisplay().getActiveShell()); 305 dlg.open(mDeviceList.getSelectedDevice()); 306 } 307 }; 308 mCaptureAction.setToolTipText(Messages.DeviceView_Screen_Capture_Tooltip); 309 mCaptureAction.setImageDescriptor(loader.loadDescriptor("capture.png")); //$NON-NLS-1$ 310 311 mResetAdbAction = new Action(Messages.DeviceView_Reset_ADB) { 312 @Override 313 public void run() { 314 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge(); 315 if (bridge != null) { 316 if (bridge.restart() == false) { 317 // get the current Display 318 final Display display = DdmsPlugin.getDisplay(); 319 320 // dialog box only run in ui thread.. 321 display.asyncExec(new Runnable() { 322 @Override 323 public void run() { 324 Shell shell = display.getActiveShell(); 325 MessageDialog.openError(shell, Messages.DeviceView_ADB_Error, 326 Messages.DeviceView_ADB_Failed_Restart); 327 } 328 }); 329 } 330 } 331 } 332 }; 333 mResetAdbAction.setToolTipText(Messages.DeviceView_Reset_ADB_Host_Deamon); 334 mResetAdbAction.setImageDescriptor(PlatformUI.getWorkbench() 335 .getSharedImages().getImageDescriptor( 336 ISharedImages.IMG_OBJS_WARN_TSK)); 337 338 mKillAppAction = new Action() { 339 @Override 340 public void run() { 341 mDeviceList.killSelectedClient(); 342 } 343 }; 344 345 mKillAppAction.setText(Messages.DeviceView_Stop_Process); 346 mKillAppAction.setToolTipText(Messages.DeviceView_Stop_Process_Tooltip); 347 mKillAppAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HALT)); 348 349 mGcAction = new Action() { 350 @Override 351 public void run() { 352 mDeviceList.forceGcOnSelectedClient(); 353 } 354 }; 355 356 mGcAction.setText(Messages.DeviceView_Cause_GC); 357 mGcAction.setToolTipText(Messages.DeviceView_Cause_GC_Tooltip); 358 mGcAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_GC)); 359 360 mHprofAction = new Action() { 361 @Override 362 public void run() { 363 mDeviceList.dumpHprof(); 364 doSelectionChanged(mDeviceList.getSelectedClient()); 365 } 366 }; 367 mHprofAction.setText(Messages.DeviceView_Dump_HPROF_File); 368 mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File_Tooltip); 369 mHprofAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HPROF)); 370 371 mUpdateHeapAction = new Action(Messages.DeviceView_Update_Heap, IAction.AS_CHECK_BOX) { 372 @Override 373 public void run() { 374 boolean enable = mUpdateHeapAction.isChecked(); 375 mDeviceList.setEnabledHeapOnSelectedClient(enable); 376 } 377 }; 378 mUpdateHeapAction.setToolTipText(Messages.DeviceView_Update_Heap_Tooltip); 379 mUpdateHeapAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_HEAP)); 380 381 mUpdateThreadAction = new Action(Messages.DeviceView_Threads, IAction.AS_CHECK_BOX) { 382 @Override 383 public void run() { 384 boolean enable = mUpdateThreadAction.isChecked(); 385 mDeviceList.setEnabledThreadOnSelectedClient(enable); 386 } 387 }; 388 mUpdateThreadAction.setToolTipText(Messages.DeviceView_Threads_Tooltip); 389 mUpdateThreadAction.setImageDescriptor(loader.loadDescriptor(DevicePanel.ICON_THREAD)); 390 391 mTracingAction = new Action() { 392 @Override 393 public void run() { 394 mDeviceList.toggleMethodProfiling(); 395 } 396 }; 397 mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling); 398 mTracingAction.setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip); 399 mTracingStartImage = loader.loadDescriptor(DevicePanel.ICON_TRACING_START); 400 mTracingStopImage = loader.loadDescriptor(DevicePanel.ICON_TRACING_STOP); 401 mTracingAction.setImageDescriptor(mTracingStartImage); 402 403 mDebugAction = new Action(Messages.DeviceView_Debug_Process) { 404 @Override 405 public void run() { 406 if (DdmsPlugin.getDefault().hasDebuggerConnectors()) { 407 Client currentClient = mDeviceList.getSelectedClient(); 408 if (currentClient != null) { 409 ClientData clientData = currentClient.getClientData(); 410 411 // make sure the client can be debugged 412 switch (clientData.getDebuggerConnectionStatus()) { 413 case ERROR: { 414 Display display = DdmsPlugin.getDisplay(); 415 Shell shell = display.getActiveShell(); 416 MessageDialog.openError(shell, 417 Messages.DeviceView_Debug_Process_Title, 418 Messages.DeviceView_Process_Debug_Already_In_Use); 419 return; 420 } 421 case ATTACHED: { 422 Display display = DdmsPlugin.getDisplay(); 423 Shell shell = display.getActiveShell(); 424 MessageDialog.openError(shell, 425 Messages.DeviceView_Debug_Process_Title, 426 Messages.DeviceView_Process_Already_Being_Debugged); 427 return; 428 } 429 } 430 431 // get the name of the client 432 String packageName = clientData.getClientDescription(); 433 if (packageName != null) { 434 435 // try all connectors till one returns true. 436 IDebuggerConnector[] connectors = 437 DdmsPlugin.getDefault().getDebuggerConnectors(); 438 439 if (connectors != null) { 440 for (IDebuggerConnector connector : connectors) { 441 try { 442 if (connector.connectDebugger(packageName, 443 currentClient.getDebuggerListenPort(), 444 DdmPreferences.getSelectedDebugPort())) { 445 return; 446 } 447 } catch (Throwable t) { 448 // ignore, we'll just not use this 449 // implementation 450 } 451 } 452 } 453 454 // if we get to this point, then we failed to find a 455 // project 456 // that matched the application to debug 457 Display display = DdmsPlugin.getDisplay(); 458 Shell shell = display.getActiveShell(); 459 MessageDialog.openError(shell, Messages.DeviceView_Debug_Process_Title, 460 String.format( 461 Messages.DeviceView_Debug_Session_Failed, 462 packageName)); 463 } 464 } 465 } 466 } 467 }; 468 mDebugAction.setToolTipText(Messages.DeviceView_Debug_Process_Tooltip); 469 mDebugAction.setImageDescriptor(loader.loadDescriptor("debug-attach.png")); //$NON-NLS-1$ 470 mDebugAction.setEnabled(DdmsPlugin.getDefault().hasDebuggerConnectors()); 471 472 placeActions(); 473 474 // disabling all action buttons 475 selectionChanged(null, null); 476 477 ClientData.setHprofDumpHandler(new HProfHandler(mParentShell)); 478 AndroidDebugBridge.addClientChangeListener(this); 479 ClientData.setMethodProfilingHandler(new MethodProfilingHandler(mParentShell) { 480 @Override 481 protected void open(String tempPath) { 482 if (DdmsPlugin.getDefault().launchTraceview(tempPath) == false) { 483 super.open(tempPath); 484 } 485 } 486 }); 487 } 488 489 @Override 490 public void setFocus() { 491 mDeviceList.setFocus(); 492 } 493 494 /** 495 * Sent when a new {@link IDevice} and {@link Client} are selected. 496 * 497 * @param selectedDevice the selected device. If null, no devices are 498 * selected. 499 * @param selectedClient The selected client. If null, no clients are 500 * selected. 501 */ 502 @Override 503 public void selectionChanged(IDevice selectedDevice, Client selectedClient) { 504 // update the buttons 505 doSelectionChanged(selectedClient); 506 doSelectionChanged(selectedDevice); 507 } 508 509 private void doSelectionChanged(Client selectedClient) { 510 // update the buttons 511 if (selectedClient != null) { 512 if (USE_SELECTED_DEBUG_PORT) { 513 // set the client as the debug client 514 selectedClient.setAsSelectedClient(); 515 } 516 517 mDebugAction.setEnabled(DdmsPlugin.getDefault().hasDebuggerConnectors()); 518 mKillAppAction.setEnabled(true); 519 mGcAction.setEnabled(true); 520 521 mUpdateHeapAction.setEnabled(true); 522 mUpdateHeapAction.setChecked(selectedClient.isHeapUpdateEnabled()); 523 524 mUpdateThreadAction.setEnabled(true); 525 mUpdateThreadAction.setChecked(selectedClient.isThreadUpdateEnabled()); 526 527 ClientData data = selectedClient.getClientData(); 528 529 if (data.hasFeature(ClientData.FEATURE_HPROF)) { 530 mHprofAction.setEnabled(data.hasPendingHprofDump() == false); 531 mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File); 532 } else { 533 mHprofAction.setEnabled(false); 534 mHprofAction 535 .setToolTipText(Messages.DeviceView_Dump_HPROF_File_Not_Supported_By_VM); 536 } 537 538 if (data.hasFeature(ClientData.FEATURE_PROFILING)) { 539 mTracingAction.setEnabled(true); 540 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) { 541 mTracingAction 542 .setToolTipText(Messages.DeviceView_Stop_Method_Profiling_Tooltip); 543 mTracingAction.setText(Messages.DeviceView_Stop_Method_Profiling); 544 mTracingAction.setImageDescriptor(mTracingStopImage); 545 } else { 546 mTracingAction 547 .setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip); 548 mTracingAction.setImageDescriptor(mTracingStartImage); 549 mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling); 550 } 551 } else { 552 mTracingAction.setEnabled(false); 553 mTracingAction.setImageDescriptor(mTracingStartImage); 554 mTracingAction 555 .setToolTipText(Messages.DeviceView_Start_Method_Profiling_Not_Suported_By_Vm); 556 mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling); 557 } 558 } else { 559 if (USE_SELECTED_DEBUG_PORT) { 560 // set the client as the debug client 561 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge(); 562 if (bridge != null) { 563 bridge.setSelectedClient(null); 564 } 565 } 566 567 mDebugAction.setEnabled(false); 568 mKillAppAction.setEnabled(false); 569 mGcAction.setEnabled(false); 570 mUpdateHeapAction.setChecked(false); 571 mUpdateHeapAction.setEnabled(false); 572 mUpdateThreadAction.setEnabled(false); 573 mUpdateThreadAction.setChecked(false); 574 mHprofAction.setEnabled(false); 575 576 mHprofAction.setEnabled(false); 577 mHprofAction.setToolTipText(Messages.DeviceView_Dump_HPROF_File); 578 579 mTracingAction.setEnabled(false); 580 mTracingAction.setImageDescriptor(mTracingStartImage); 581 mTracingAction.setToolTipText(Messages.DeviceView_Start_Method_Profiling_Tooltip); 582 mTracingAction.setText(Messages.DeviceView_Start_Method_Profiling); 583 } 584 } 585 586 private void doSelectionChanged(IDevice selectedDevice) { 587 mCaptureAction.setEnabled(selectedDevice != null); 588 } 589 590 /** 591 * Place the actions in the ui. 592 */ 593 private final void placeActions() { 594 IActionBars actionBars = getViewSite().getActionBars(); 595 596 // first in the menu 597 IMenuManager menuManager = actionBars.getMenuManager(); 598 menuManager.removeAll(); 599 menuManager.add(mDebugAction); 600 menuManager.add(new Separator()); 601 menuManager.add(mUpdateHeapAction); 602 menuManager.add(mHprofAction); 603 menuManager.add(mGcAction); 604 menuManager.add(new Separator()); 605 menuManager.add(mUpdateThreadAction); 606 menuManager.add(mTracingAction); 607 menuManager.add(new Separator()); 608 menuManager.add(mKillAppAction); 609 menuManager.add(new Separator()); 610 menuManager.add(mCaptureAction); 611 menuManager.add(new Separator()); 612 menuManager.add(mResetAdbAction); 613 614 // and then in the toolbar 615 IToolBarManager toolBarManager = actionBars.getToolBarManager(); 616 toolBarManager.removeAll(); 617 toolBarManager.add(mDebugAction); 618 toolBarManager.add(new Separator()); 619 toolBarManager.add(mUpdateHeapAction); 620 toolBarManager.add(mHprofAction); 621 toolBarManager.add(mGcAction); 622 toolBarManager.add(new Separator()); 623 toolBarManager.add(mUpdateThreadAction); 624 toolBarManager.add(mTracingAction); 625 toolBarManager.add(new Separator()); 626 toolBarManager.add(mKillAppAction); 627 toolBarManager.add(new Separator()); 628 toolBarManager.add(mCaptureAction); 629 } 630 631 @Override 632 public void clientChanged(final Client client, int changeMask) { 633 if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) == Client.CHANGE_METHOD_PROFILING_STATUS) { 634 if (mDeviceList.getSelectedClient() == client) { 635 mParentShell.getDisplay().asyncExec(new Runnable() { 636 @Override 637 public void run() { 638 // force refresh of the button enabled state. 639 doSelectionChanged(client); 640 } 641 }); 642 } 643 } 644 } 645 } 646