2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.hierarchyviewer.device;
19 import com.android.ddmlib.AdbCommandRejectedException;
20 import com.android.ddmlib.AndroidDebugBridge;
21 import com.android.ddmlib.IDevice;
22 import com.android.ddmlib.Log;
23 import com.android.ddmlib.MultiLineReceiver;
24 import com.android.ddmlib.TimeoutException;
26 import java.io.IOException;
28 import java.util.HashMap;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
32 public class DeviceBridge {
33 private static AndroidDebugBridge bridge;
35 private static final HashMap<IDevice, Integer> devicePortMap = new HashMap<IDevice, Integer>();
36 private static int nextLocalPort = Configuration.DEFAULT_SERVER_PORT;
38 public static void initDebugBridge() {
40 AndroidDebugBridge.init(false /* debugger support */);
42 if (bridge == null || !bridge.isConnected()) {
43 String adbLocation = System.getProperty("hierarchyviewer.adb");
44 if (adbLocation != null && adbLocation.length() != 0) {
45 adbLocation += File.separator + "adb";
50 bridge = AndroidDebugBridge.createBridge(adbLocation, true);
54 public static void startListenForDevices(AndroidDebugBridge.IDeviceChangeListener listener) {
55 AndroidDebugBridge.addDeviceChangeListener(listener);
58 public static void stopListenForDevices(AndroidDebugBridge.IDeviceChangeListener listener) {
59 AndroidDebugBridge.removeDeviceChangeListener(listener);
62 public static IDevice[] getDevices() {
63 return bridge.getDevices();
66 public static boolean isViewServerRunning(IDevice device) {
68 final boolean[] result = new boolean[1];
70 if (device.isOnline()) {
71 device.executeShellCommand(buildIsServerRunningShellCommand(),
72 new BooleanResultReader(result));
74 } catch (IOException e) {
80 public static boolean startViewServer(IDevice device) {
81 return startViewServer(device, Configuration.DEFAULT_SERVER_PORT);
84 public static boolean startViewServer(IDevice device, int port) {
86 final boolean[] result = new boolean[1];
88 if (device.isOnline()) {
89 device.executeShellCommand(buildStartServerShellCommand(port),
90 new BooleanResultReader(result));
92 } catch (IOException e) {
98 public static boolean stopViewServer(IDevice device) {
100 final boolean[] result = new boolean[1];
102 if (device.isOnline()) {
103 device.executeShellCommand(buildStopServerShellCommand(),
104 new BooleanResultReader(result));
106 } catch (IOException e) {
112 public static void terminate() {
113 AndroidDebugBridge.terminate();
117 * Sets up a just-connected device to work with the view server.
118 * <p/>This starts a port forwarding between a local port and a port on the device.
121 public static void setupDeviceForward(IDevice device) {
122 synchronized (devicePortMap) {
123 if (device.getState() == IDevice.DeviceState.ONLINE) {
124 int localPort = nextLocalPort++;
126 device.createForward(localPort, Configuration.DEFAULT_SERVER_PORT);
127 devicePortMap.put(device, localPort);
128 } catch (TimeoutException e) {
129 Log.e("hierarchy", "Timeout setting up port forwarding for " + device);
130 } catch (AdbCommandRejectedException e) {
131 Log.e("hierarchy", String.format(
132 "Adb rejected forward command for device %1$s: %2$s",
133 device, e.getMessage()));
134 } catch (IOException e) {
135 Log.e("hierarchy", String.format(
136 "Failed to create forward for device %1$s: %2$s",
137 device, e.getMessage()));
143 public static void removeDeviceForward(IDevice device) {
144 synchronized (devicePortMap) {
145 final Integer localPort = devicePortMap.get(device);
146 if (localPort != null) {
148 device.removeForward(localPort, Configuration.DEFAULT_SERVER_PORT);
149 devicePortMap.remove(device);
150 } catch (TimeoutException e) {
151 Log.e("hierarchy", "Timeout removing port forwarding for " + device);
152 } catch (AdbCommandRejectedException e) {
153 Log.e("hierarchy", String.format(
154 "Adb rejected remove-forward command for device %1$s: %2$s",
155 device, e.getMessage()));
156 } catch (IOException e) {
157 Log.e("hierarchy", String.format(
158 "Failed to remove forward for device %1$s: %2$s",
159 device, e.getMessage()));
165 public static int getDeviceLocalPort(IDevice device) {
166 synchronized (devicePortMap) {
167 Integer port = devicePortMap.get(device);
172 Log.e("hierarchy", "Missing forwarded port for " + device.getSerialNumber());
178 private static String buildStartServerShellCommand(int port) {
179 return String.format("service call window %d i32 %d",
180 Configuration.SERVICE_CODE_START_SERVER, port);
183 private static String buildStopServerShellCommand() {
184 return String.format("service call window %d", Configuration.SERVICE_CODE_STOP_SERVER);
187 private static String buildIsServerRunningShellCommand() {
188 return String.format("service call window %d",
189 Configuration.SERVICE_CODE_IS_SERVER_RUNNING);
192 private static class BooleanResultReader extends MultiLineReceiver {
193 private final boolean[] mResult;
195 public BooleanResultReader(boolean[] result) {
200 public void processNewLines(String[] strings) {
201 if (strings.length > 0) {
202 Pattern pattern = Pattern.compile(".*?\\([0-9]{8} ([0-9]{8}).*");
203 Matcher matcher = pattern.matcher(strings[0]);
204 if (matcher.matches()) {
205 if (Integer.parseInt(matcher.group(1)) == 1) {
212 public boolean isCancelled() {