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