Home | History | Annotate | Download | only in device
      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