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.hierarchyviewer.device; 18 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; 25 26 import java.io.IOException; 27 import java.io.File; 28 import java.util.HashMap; 29 import java.util.regex.Matcher; 30 import java.util.regex.Pattern; 31 32 public class DeviceBridge { 33 private static AndroidDebugBridge bridge; 34 35 private static final HashMap<IDevice, Integer> devicePortMap = new HashMap<IDevice, Integer>(); 36 private static int nextLocalPort = Configuration.DEFAULT_SERVER_PORT; 37 38 public static void initDebugBridge() { 39 if (bridge == null) { 40 AndroidDebugBridge.init(false /* debugger support */); 41 } 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"; 46 } else { 47 adbLocation = "adb"; 48 } 49 50 bridge = AndroidDebugBridge.createBridge(adbLocation, true); 51 } 52 } 53 54 public static void startListenForDevices(AndroidDebugBridge.IDeviceChangeListener listener) { 55 AndroidDebugBridge.addDeviceChangeListener(listener); 56 } 57 58 public static void stopListenForDevices(AndroidDebugBridge.IDeviceChangeListener listener) { 59 AndroidDebugBridge.removeDeviceChangeListener(listener); 60 } 61 62 public static IDevice[] getDevices() { 63 return bridge.getDevices(); 64 } 65 66 public static boolean isViewServerRunning(IDevice device) { 67 initDebugBridge(); 68 final boolean[] result = new boolean[1]; 69 try { 70 if (device.isOnline()) { 71 device.executeShellCommand(buildIsServerRunningShellCommand(), 72 new BooleanResultReader(result)); 73 } 74 } catch (IOException e) { 75 e.printStackTrace(); 76 } 77 return result[0]; 78 } 79 80 public static boolean startViewServer(IDevice device) { 81 return startViewServer(device, Configuration.DEFAULT_SERVER_PORT); 82 } 83 84 public static boolean startViewServer(IDevice device, int port) { 85 initDebugBridge(); 86 final boolean[] result = new boolean[1]; 87 try { 88 if (device.isOnline()) { 89 device.executeShellCommand(buildStartServerShellCommand(port), 90 new BooleanResultReader(result)); 91 } 92 } catch (IOException e) { 93 e.printStackTrace(); 94 } 95 return result[0]; 96 } 97 98 public static boolean stopViewServer(IDevice device) { 99 initDebugBridge(); 100 final boolean[] result = new boolean[1]; 101 try { 102 if (device.isOnline()) { 103 device.executeShellCommand(buildStopServerShellCommand(), 104 new BooleanResultReader(result)); 105 } 106 } catch (IOException e) { 107 e.printStackTrace(); 108 } 109 return result[0]; 110 } 111 112 public static void terminate() { 113 AndroidDebugBridge.terminate(); 114 } 115 116 /** 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. 119 * @param device 120 */ 121 public static void setupDeviceForward(IDevice device) { 122 synchronized (devicePortMap) { 123 if (device.getState() == IDevice.DeviceState.ONLINE) { 124 int localPort = nextLocalPort++; 125 try { 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())); 138 } 139 } 140 } 141 } 142 143 public static void removeDeviceForward(IDevice device) { 144 synchronized (devicePortMap) { 145 final Integer localPort = devicePortMap.get(device); 146 if (localPort != null) { 147 try { 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())); 160 } 161 } 162 } 163 } 164 165 public static int getDeviceLocalPort(IDevice device) { 166 synchronized (devicePortMap) { 167 Integer port = devicePortMap.get(device); 168 if (port != null) { 169 return port; 170 } 171 172 Log.e("hierarchy", "Missing forwarded port for " + device.getSerialNumber()); 173 return -1; 174 } 175 176 } 177 178 private static String buildStartServerShellCommand(int port) { 179 return String.format("service call window %d i32 %d", 180 Configuration.SERVICE_CODE_START_SERVER, port); 181 } 182 183 private static String buildStopServerShellCommand() { 184 return String.format("service call window %d", Configuration.SERVICE_CODE_STOP_SERVER); 185 } 186 187 private static String buildIsServerRunningShellCommand() { 188 return String.format("service call window %d", 189 Configuration.SERVICE_CODE_IS_SERVER_RUNNING); 190 } 191 192 private static class BooleanResultReader extends MultiLineReceiver { 193 private final boolean[] mResult; 194 195 public BooleanResultReader(boolean[] result) { 196 mResult = result; 197 } 198 199 @Override 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) { 206 mResult[0] = true; 207 } 208 } 209 } 210 } 211 212 public boolean isCancelled() { 213 return false; 214 } 215 } 216 } 217