最近在看一个老外写的东西,发现里面有个类,使用这个类可以让任何设备使用HierarchyView。

众所周知,市面上卖的Android设备,一般都不能使用HierarchyView,所以借此机会,了解一下HierarchyView的实现原理,并学习一下老外的解决方法。

HierarchyView的源码在/sdk/eclipse/plugins/com.android.ide.eclipse.hierarchyviewer中,但貌似不全,

所以直接反编译/prebuilts/devtools/tools/lib/hierarchyviewer2lib.jar和/prebuilts/devtools/tools/lib/hierarchyviewer2.jar。

当对设备使用HierarchyView时,HierarchyView会给设备发送一个startViewServer的命令,下面源码时其调用顺序:

HierarchyViewerDirector.class

  public void populateDeviceSelectionModel() {
    IDevice[] devices = DeviceBridge.getDevices();
    for (IDevice device : devices)
      deviceConnected(device);
  }

  public void deviceConnected(final IDevice device)
  {
    executeInBackground("Connecting device", new Object()
    {
      public void run() {
        if (!device.isOnline())
          return;
        IHvDevice hvDevice;
        synchronized (HierarchyViewerDirector.mDevicesLock) {
          hvDevice = (IHvDevice)HierarchyViewerDirector.this.mDevices.get(device);
          if (hvDevice == null) {
            hvDevice = HvDeviceFactory.create(device);
            hvDevice.initializeViewDebug();
            hvDevice.addWindowChangeListener(HierarchyViewerDirector.getDirector());
            HierarchyViewerDirector.this.mDevices.put(device, hvDevice);
          }
          else {
            hvDevice.initializeViewDebug();
          }
        }

        DeviceSelectionModel.getModel().addDevice(hvDevice);
        HierarchyViewerDirector.this.focusChanged(device);
      }
    });
  }

 

ViewServerDevice.class

  public boolean initializeViewDebug()
  {
    if (!this.mDevice.isOnline()) {
      return false;
    }

    DeviceBridge.setupDeviceForward(this.mDevice);

    return reloadWindows();
  }

  public boolean reloadWindows()
  {
    if ((!DeviceBridge.isViewServerRunning(this.mDevice)) && 
      (!DeviceBridge.startViewServer(this.mDevice))) {
      Log.e("ViewServerDevice", "Unable to debug device: " + this.mDevice.getName());
      DeviceBridge.removeDeviceForward(this.mDevice);
      return false;
    }

    this.mViewServerInfo = DeviceBridge.loadViewServerInfo(this.mDevice);
    if (this.mViewServerInfo == null) {
      return false;
    }

    this.mWindows = DeviceBridge.loadWindows(this, this.mDevice);
    return true;
  }

 

 

DeviceBridge.class

  public static boolean startViewServer(IDevice device) {
    return startViewServer(device, 4939);
  }

  public static boolean startViewServer(IDevice device, int port) {
    boolean[] result = new boolean[1];
    try {
      if (device.isOnline())
        device.executeShellCommand(buildStartServerShellCommand(port), new BooleanResultReader(result));
    }
    catch (TimeoutException e)
    {
      Log.e("hierarchyviewer", "Timeout starting view server on device " + device);
    } catch (IOException e) {
      Log.e("hierarchyviewer", "Unable to start view server on device " + device);
    } catch (AdbCommandRejectedException e) {
      Log.e("hierarchyviewer", "Adb rejected command to start view server on device " + device);
    } catch (ShellCommandUnresponsiveException e) {
      Log.e("hierarchyviewer", "Unable to execute command to start view server on device " + device);
    }
    return result[0];
  }

  private static String buildStartServerShellCommand(int port) {
    return String.format("service call window %d i32 %d", new Object[] { Integer.valueOf(1), Integer.valueOf(port) });
  }

 

 

从代码中可以看到,最终HierarchyView会让设备执行service命令,最终执行的命令是这样:

shell@device:/ $ service call window 1 i32 4939

这行命令其实是向android.view.IWindowManager发送一个CODE为1,值为4939的parcel。

其实就是调用WindowManagerService中的startViewServer方法,并把4939作为参数传入,接下来看看WindowManagerService.startViewServer的源码:

    public boolean startViewServer(int port) {
        if (isSystemSecure()) {
            return false;
        }

        if (!checkCallingPermission(Manifest.permission.DUMP, "startViewServer")) {
            return false;
        }

        if (port < 1024) {
            return false;
        }

        if (mViewServer != null) {
            if (!mViewServer.isRunning()) {
                try {
                    return mViewServer.start();
                } catch (IOException e) {
                    Slog.w(TAG, "View server did not start");
                }
            }
            return false;
        }

        try {
            mViewServer = new ViewServer(this, port);
            return mViewServer.start();
        } catch (IOException e) {
            Slog.w(TAG, "View server did not start");
        }
        return false;
    }

    private boolean isSystemSecure() {
        return "1".equals(SystemProperties.get(SYSTEM_SECURE, "1")) &&
                "0".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
    }

 

里面会做一些权限检查,然后会调用ViewServer.start(),关键就在ViewServer里,先吧ViewServer完整的代码贴上:

  1 /*
  2  * Copyright (C) 2007 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.server.wm;
 18 
 19 
 20 import android.util.Slog;
 21 
 22 import java.net.ServerSocket;
 23 import java.net.Socket;
 24 import java.net.InetAddress;
 25 import java.util.concurrent.ExecutorService;
 26 import java.util.concurrent.Executors;
 27 import java.io.IOException;
 28 import java.io.BufferedReader;
 29 import java.io.InputStreamReader;
 30 import java.io.OutputStream;
 31 import java.io.BufferedWriter;
 32 import java.io.OutputStreamWriter;
 33 
 34 /**
 35  * The ViewServer is local socket server that can be used to communicate with the
 36  * views of the opened windows. Communication with the views is ensured by the
 37  * {@link com.android.server.wm.WindowManagerService} and is a cross-process operation.
 38  *
 39  * {@hide}
 40  */
 41 class ViewServer implements Runnable {
 42     /**
 43      * The default port used to start view servers.
 44      */
 45     public static final int VIEW_SERVER_DEFAULT_PORT = 4939;
 46 
 47     private static final int VIEW_SERVER_MAX_CONNECTIONS = 10;
 48 
 49     // Debug facility
 50     private static final String LOG_TAG = "ViewServer";
 51 
 52     private static final String VALUE_PROTOCOL_VERSION = "4";
 53     private static final String VALUE_SERVER_VERSION = "4";
 54 
 55     // Protocol commands
 56     // Returns the protocol version
 57     private static final String COMMAND_PROTOCOL_VERSION = "PROTOCOL";
 58     // Returns the server version
 59     private static final String COMMAND_SERVER_VERSION = "SERVER";
 60     // Lists all of the available windows in the system
 61     private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
 62     // Keeps a connection open and notifies when the list of windows changes
 63     private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
 64     // Returns the focused window
 65     private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
 66 
 67     private ServerSocket mServer;
 68     private Thread mThread;
 69 
 70     private final WindowManagerService mWindowManager;
 71     private final int mPort;
 72 
 73     private ExecutorService mThreadPool;
 74 
 75     /**
 76      * Creates a new ViewServer associated with the specified window manager on the
 77      * specified local port. The server is not started by default.
 78      *
 79      * @param windowManager The window manager used to communicate with the views.
 80      * @param port The port for the server to listen to.
 81      *
 82      * @see #start()
 83      */
 84     ViewServer(WindowManagerService windowManager, int port) {
 85         mWindowManager = windowManager;
 86         mPort = port;
 87     }
 88 
 89     /**
 90      * Starts the server.
 91      *
 92      * @return True if the server was successfully created, or false if it already exists.
 93      * @throws IOException If the server cannot be created.
 94      *
 95      * @see #stop()
 96      * @see #isRunning()
 97      * @see WindowManagerService#startViewServer(int)
 98      */
 99     boolean start() throws IOException {
100         if (mThread != null) {
101             return false;
102         }
103 
104         mServer = new ServerSocket(mPort, VIEW_SERVER_MAX_CONNECTIONS, InetAddress.getLocalHost());
105         mThread = new Thread(this, "Remote View Server [port=" + mPort + "]");
106         mThreadPool = Executors.newFixedThreadPool(VIEW_SERVER_MAX_CONNECTIONS);
107         mThread.start();
108 
109         return true;
110     }
111 
112     /**
113      * Stops the server.
114      *
115      * @return True if the server was stopped, false if an error occured or if the
116      *         server wasn't started.
117      *
118      * @see #start()
119      * @see #isRunning()
120      * @see WindowManagerService#stopViewServer()
121      */
122     boolean stop() {
123         if (mThread != null) {
124 
125             mThread.interrupt();
126             if (mThreadPool != null) {
127                 try {
128                     mThreadPool.shutdownNow();
129                 } catch (SecurityException e) {
130                     Slog.w(LOG_TAG, "Could not stop all view server threads");
131                 }
132             }
133             mThreadPool = null;
134             mThread = null;
135             try {
136                 mServer.close();
137                 mServer = null;
138                 return true;
139             } catch (IOException e) {
140                 Slog.w(LOG_TAG, "Could not close the view server");
141             }
142         }
143         return false;
144     }
145 
146     /**
147      * Indicates whether the server is currently running.
148      *
149      * @return True if the server is running, false otherwise.
150      *
151      * @see #start()
152      * @see #stop()
153      * @see WindowManagerService#isViewServerRunning()  
154      */
155     boolean isRunning() {
156         return mThread != null && mThread.isAlive();
157     }
158 
159     /**
160      * Main server loop.
161      */
162     public void run() {
163         while (Thread.currentThread() == mThread) {
164             // Any uncaught exception will crash the system process
165             try {
166                 Socket client = mServer.accept();
167                 if (mThreadPool != null) {
168                     mThreadPool.submit(new ViewServerWorker(client));
169                 } else {
170                     try {
171                         client.close();
172                     } catch (IOException e) {
173                         e.printStackTrace();
174                     }
175                 }
176             } catch (Exception e) {
177                 Slog.w(LOG_TAG, "Connection error: ", e);
178             }
179         }
180     }
181 
182     private static boolean writeValue(Socket client, String value) {
183         boolean result;
184         BufferedWriter out = null;
185         try {
186             OutputStream clientStream = client.getOutputStream();
187             out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
188             out.write(value);
189             out.write("\n");
190             out.flush();
191             result = true;
192         } catch (Exception e) {
193             result = false;
194         } finally {
195             if (out != null) {
196                 try {
197                     out.close();
198                 } catch (IOException e) {
199                     result = false;
200                 }
201             }
202         }
203         return result;
204     }
205 
206     class ViewServerWorker implements Runnable, WindowManagerService.WindowChangeListener {
207         private Socket mClient;
208         private boolean mNeedWindowListUpdate;
209         private boolean mNeedFocusedWindowUpdate;
210 
211         public ViewServerWorker(Socket client) {
212             mClient = client;
213             mNeedWindowListUpdate = false;
214             mNeedFocusedWindowUpdate = false;
215         }
216 
217         public void run() {
218 
219             BufferedReader in = null;
220             try {
221                 in = new BufferedReader(new InputStreamReader(mClient.getInputStream()), 1024);
222 
223                 final String request = in.readLine();
224 
225                 String command;
226                 String parameters;
227 
228                 int index = request.indexOf(' ');
229                 if (index == -1) {
230                     command = request;
231                     parameters = "";
232                 } else {
233                     command = request.substring(0, index);
234                     parameters = request.substring(index + 1);
235                 }
236 
237                 boolean result;
238                 if (COMMAND_PROTOCOL_VERSION.equalsIgnoreCase(command)) {
239                     result = writeValue(mClient, VALUE_PROTOCOL_VERSION);
240                 } else if (COMMAND_SERVER_VERSION.equalsIgnoreCase(command)) {
241                     result = writeValue(mClient, VALUE_SERVER_VERSION);
242                 } else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
243                     result = mWindowManager.viewServerListWindows(mClient);
244                 } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
245                     result = mWindowManager.viewServerGetFocusedWindow(mClient);
246                 } else if (COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
247                     result = windowManagerAutolistLoop();
248                 } else {
249                     result = mWindowManager.viewServerWindowCommand(mClient,
250                             command, parameters);
251                 }
252 
253                 if (!result) {
254                     Slog.w(LOG_TAG, "An error occurred with the command: " + command);
255                 }
256             } catch(IOException e) {
257                 Slog.w(LOG_TAG, "Connection error: ", e);
258             } finally {
259                 if (in != null) {
260                     try {
261                         in.close();
262 
263                     } catch (IOException e) {
264                         e.printStackTrace();
265                     }
266                 }
267                 if (mClient != null) {
268                     try {
269                         mClient.close();
270                     } catch (IOException e) {
271                         e.printStackTrace();
272                     }
273                 }
274             }
275         }
276 
277         public void windowsChanged() {
278             synchronized(this) {
279                 mNeedWindowListUpdate = true;
280                 notifyAll();
281             }
282         }
283 
284         public void focusChanged() {
285             synchronized(this) {
286                 mNeedFocusedWindowUpdate = true;
287                 notifyAll();
288             }
289         }
290 
291         private boolean windowManagerAutolistLoop() {
292             mWindowManager.addWindowChangeListener(this);
293             BufferedWriter out = null;
294             try {
295                 out = new BufferedWriter(new OutputStreamWriter(mClient.getOutputStream()));
296                 while (!Thread.interrupted()) {
297                     boolean needWindowListUpdate = false;
298                     boolean needFocusedWindowUpdate = false;
299                     synchronized (this) {
300                         while (!mNeedWindowListUpdate && !mNeedFocusedWindowUpdate) {
301                             wait();
302                         }
303                         if (mNeedWindowListUpdate) {
304                             mNeedWindowListUpdate = false;
305                             needWindowListUpdate = true;
306                         }
307                         if (mNeedFocusedWindowUpdate) {
308                             mNeedFocusedWindowUpdate = false;
309                             needFocusedWindowUpdate = true;
310                         }
311                     }
312                     if (needWindowListUpdate) {
313                         out.write("LIST UPDATE\n");
314                         out.flush();
315                     }
316                     if (needFocusedWindowUpdate) {
317                         out.write("ACTION_FOCUS UPDATE\n");
318                         out.flush();
319                     }
320                 }
321             } catch (Exception e) {
322                 // Ignore
323             } finally {
324                 if (out != null) {
325                     try {
326                         out.close();
327                     } catch (IOException e) {
328                         // Ignore
329                     }
330                 }
331                 mWindowManager.removeWindowChangeListener(this);
332             }
333             return true;
334         }
335     }
336 }
ViewServer.java

相关文章:

  • 2021-08-17
  • 2021-11-04
  • 2021-08-30
  • 2022-12-23
  • 2021-08-17
  • 2021-09-17
  • 2021-10-29
猜你喜欢
  • 2021-10-10
  • 2021-12-22
  • 2022-02-12
  • 2021-07-28
  • 2022-12-23
  • 2022-01-15
  • 2021-12-01
相关资源
相似解决方案