• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ddmlib;
18 
19 import com.android.ddmlib.SyncService.SyncResult;
20 import com.android.ddmlib.log.LogReceiver;
21 
22 import java.io.File;
23 import java.io.IOException;
24 import java.nio.channels.SocketChannel;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32 
33 
34 /**
35  * A Device. It can be a physical device or an emulator.
36  */
37 final class Device implements IDevice {
38 
39     /** Emulator Serial Number regexp. */
40     final static String RE_EMULATOR_SN = "emulator-(\\d+)"; //$NON-NLS-1$
41 
42     /** Serial number of the device */
43     private String mSerialNumber = null;
44 
45     /** Name of the AVD */
46     private String mAvdName = null;
47 
48     /** State of the device. */
49     private DeviceState mState = null;
50 
51     /** Device properties. */
52     private final Map<String, String> mProperties = new HashMap<String, String>();
53 
54     private final ArrayList<Client> mClients = new ArrayList<Client>();
55     private DeviceMonitor mMonitor;
56 
57     private static final String LOG_TAG = "Device";
58 
59     /**
60      * Socket for the connection monitoring client connection/disconnection.
61      */
62     private SocketChannel mSocketChannel;
63 
64     /**
65      * Output receiver for "pm install package.apk" command line.
66      */
67     private static final class InstallReceiver extends MultiLineReceiver {
68 
69         private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$
70         private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$
71 
72         private String mErrorMessage = null;
73 
InstallReceiver()74         public InstallReceiver() {
75         }
76 
77         @Override
processNewLines(String[] lines)78         public void processNewLines(String[] lines) {
79             for (String line : lines) {
80                 if (line.length() > 0) {
81                     if (line.startsWith(SUCCESS_OUTPUT)) {
82                         mErrorMessage = null;
83                     } else {
84                         Matcher m = FAILURE_PATTERN.matcher(line);
85                         if (m.matches()) {
86                             mErrorMessage = m.group(1);
87                         }
88                     }
89                 }
90             }
91         }
92 
isCancelled()93         public boolean isCancelled() {
94             return false;
95         }
96 
getErrorMessage()97         public String getErrorMessage() {
98             return mErrorMessage;
99         }
100     }
101 
102     /*
103      * (non-Javadoc)
104      * @see com.android.ddmlib.IDevice#getSerialNumber()
105      */
getSerialNumber()106     public String getSerialNumber() {
107         return mSerialNumber;
108     }
109 
110     /** {@inheritDoc} */
getAvdName()111     public String getAvdName() {
112         return mAvdName;
113     }
114 
115     /**
116      * Sets the name of the AVD
117      */
setAvdName(String avdName)118     void setAvdName(String avdName) {
119         if (isEmulator() == false) {
120             throw new IllegalArgumentException(
121                     "Cannot set the AVD name of the device is not an emulator");
122         }
123 
124         mAvdName = avdName;
125     }
126 
127     /*
128      * (non-Javadoc)
129      * @see com.android.ddmlib.IDevice#getState()
130      */
getState()131     public DeviceState getState() {
132         return mState;
133     }
134 
135     /**
136      * Changes the state of the device.
137      */
setState(DeviceState state)138     void setState(DeviceState state) {
139         mState = state;
140     }
141 
142 
143     /*
144      * (non-Javadoc)
145      * @see com.android.ddmlib.IDevice#getProperties()
146      */
getProperties()147     public Map<String, String> getProperties() {
148         return Collections.unmodifiableMap(mProperties);
149     }
150 
151     /*
152      * (non-Javadoc)
153      * @see com.android.ddmlib.IDevice#getPropertyCount()
154      */
getPropertyCount()155     public int getPropertyCount() {
156         return mProperties.size();
157     }
158 
159     /*
160      * (non-Javadoc)
161      * @see com.android.ddmlib.IDevice#getProperty(java.lang.String)
162      */
getProperty(String name)163     public String getProperty(String name) {
164         return mProperties.get(name);
165     }
166 
167 
168     @Override
toString()169     public String toString() {
170         return mSerialNumber;
171     }
172 
173     /*
174      * (non-Javadoc)
175      * @see com.android.ddmlib.IDevice#isOnline()
176      */
isOnline()177     public boolean isOnline() {
178         return mState == DeviceState.ONLINE;
179     }
180 
181     /*
182      * (non-Javadoc)
183      * @see com.android.ddmlib.IDevice#isEmulator()
184      */
isEmulator()185     public boolean isEmulator() {
186         return mSerialNumber.matches(RE_EMULATOR_SN);
187     }
188 
189     /*
190      * (non-Javadoc)
191      * @see com.android.ddmlib.IDevice#isOffline()
192      */
isOffline()193     public boolean isOffline() {
194         return mState == DeviceState.OFFLINE;
195     }
196 
197     /*
198      * (non-Javadoc)
199      * @see com.android.ddmlib.IDevice#isBootLoader()
200      */
isBootLoader()201     public boolean isBootLoader() {
202         return mState == DeviceState.BOOTLOADER;
203     }
204 
205     /*
206      * (non-Javadoc)
207      * @see com.android.ddmlib.IDevice#hasClients()
208      */
hasClients()209     public boolean hasClients() {
210         return mClients.size() > 0;
211     }
212 
213     /*
214      * (non-Javadoc)
215      * @see com.android.ddmlib.IDevice#getClients()
216      */
getClients()217     public Client[] getClients() {
218         synchronized (mClients) {
219             return mClients.toArray(new Client[mClients.size()]);
220         }
221     }
222 
223     /*
224      * (non-Javadoc)
225      * @see com.android.ddmlib.IDevice#getClient(java.lang.String)
226      */
getClient(String applicationName)227     public Client getClient(String applicationName) {
228         synchronized (mClients) {
229             for (Client c : mClients) {
230                 if (applicationName.equals(c.getClientData().getClientDescription())) {
231                     return c;
232                 }
233             }
234 
235         }
236 
237         return null;
238     }
239 
240     /*
241      * (non-Javadoc)
242      * @see com.android.ddmlib.IDevice#getSyncService()
243      */
getSyncService()244     public SyncService getSyncService() throws IOException {
245         SyncService syncService = new SyncService(AndroidDebugBridge.sSocketAddr, this);
246         if (syncService.openSync()) {
247             return syncService;
248          }
249 
250         return null;
251     }
252 
253     /*
254      * (non-Javadoc)
255      * @see com.android.ddmlib.IDevice#getFileListingService()
256      */
getFileListingService()257     public FileListingService getFileListingService() {
258         return new FileListingService(this);
259     }
260 
261     /*
262      * (non-Javadoc)
263      * @see com.android.ddmlib.IDevice#getScreenshot()
264      */
getScreenshot()265     public RawImage getScreenshot() throws IOException {
266         return AdbHelper.getFrameBuffer(AndroidDebugBridge.sSocketAddr, this);
267     }
268 
269     /*
270      * (non-Javadoc)
271      * @see com.android.ddmlib.IDevice#executeShellCommand(java.lang.String, com.android.ddmlib.IShellOutputReceiver)
272      */
executeShellCommand(String command, IShellOutputReceiver receiver)273     public void executeShellCommand(String command, IShellOutputReceiver receiver)
274             throws IOException {
275         AdbHelper.executeRemoteCommand(AndroidDebugBridge.sSocketAddr, command, this,
276                 receiver);
277     }
278 
279     /*
280      * (non-Javadoc)
281      * @see com.android.ddmlib.IDevice#runEventLogService(com.android.ddmlib.log.LogReceiver)
282      */
runEventLogService(LogReceiver receiver)283     public void runEventLogService(LogReceiver receiver) throws IOException {
284         AdbHelper.runEventLogService(AndroidDebugBridge.sSocketAddr, this, receiver);
285     }
286 
287     /*
288      * (non-Javadoc)
289      * @see com.android.ddmlib.IDevice#runLogService(com.android.ddmlib.log.LogReceiver)
290      */
runLogService(String logname, LogReceiver receiver)291     public void runLogService(String logname,
292             LogReceiver receiver) throws IOException {
293         AdbHelper.runLogService(AndroidDebugBridge.sSocketAddr, this, logname, receiver);
294     }
295 
296     /*
297      * (non-Javadoc)
298      * @see com.android.ddmlib.IDevice#createForward(int, int)
299      */
createForward(int localPort, int remotePort)300     public boolean createForward(int localPort, int remotePort) {
301         try {
302             return AdbHelper.createForward(AndroidDebugBridge.sSocketAddr, this,
303                     localPort, remotePort);
304         } catch (IOException e) {
305             Log.e("adb-forward", e); //$NON-NLS-1$
306             return false;
307         }
308     }
309 
310     /*
311      * (non-Javadoc)
312      * @see com.android.ddmlib.IDevice#removeForward(int, int)
313      */
removeForward(int localPort, int remotePort)314     public boolean removeForward(int localPort, int remotePort) {
315         try {
316             return AdbHelper.removeForward(AndroidDebugBridge.sSocketAddr, this,
317                     localPort, remotePort);
318         } catch (IOException e) {
319             Log.e("adb-remove-forward", e); //$NON-NLS-1$
320             return false;
321         }
322     }
323 
324     /*
325      * (non-Javadoc)
326      * @see com.android.ddmlib.IDevice#getClientName(int)
327      */
getClientName(int pid)328     public String getClientName(int pid) {
329         synchronized (mClients) {
330             for (Client c : mClients) {
331                 if (c.getClientData().getPid() == pid) {
332                     return c.getClientData().getClientDescription();
333                 }
334             }
335         }
336 
337         return null;
338     }
339 
340 
Device(DeviceMonitor monitor, String serialNumber, DeviceState deviceState)341     Device(DeviceMonitor monitor, String serialNumber, DeviceState deviceState) {
342         mMonitor = monitor;
343         mSerialNumber = serialNumber;
344         mState = deviceState;
345     }
346 
getMonitor()347     DeviceMonitor getMonitor() {
348         return mMonitor;
349     }
350 
addClient(Client client)351     void addClient(Client client) {
352         synchronized (mClients) {
353             mClients.add(client);
354         }
355     }
356 
getClientList()357     List<Client> getClientList() {
358         return mClients;
359     }
360 
hasClient(int pid)361     boolean hasClient(int pid) {
362         synchronized (mClients) {
363             for (Client client : mClients) {
364                 if (client.getClientData().getPid() == pid) {
365                     return true;
366                 }
367             }
368         }
369 
370         return false;
371     }
372 
clearClientList()373     void clearClientList() {
374         synchronized (mClients) {
375             mClients.clear();
376         }
377     }
378 
379     /**
380      * Sets the client monitoring socket.
381      * @param socketChannel the sockets
382      */
setClientMonitoringSocket(SocketChannel socketChannel)383     void setClientMonitoringSocket(SocketChannel socketChannel) {
384         mSocketChannel = socketChannel;
385     }
386 
387     /**
388      * Returns the client monitoring socket.
389      */
getClientMonitoringSocket()390     SocketChannel getClientMonitoringSocket() {
391         return mSocketChannel;
392     }
393 
394     /**
395      * Removes a {@link Client} from the list.
396      * @param client the client to remove.
397      * @param notify Whether or not to notify the listeners of a change.
398      */
removeClient(Client client, boolean notify)399     void removeClient(Client client, boolean notify) {
400         mMonitor.addPortToAvailableList(client.getDebuggerListenPort());
401         synchronized (mClients) {
402             mClients.remove(client);
403         }
404         if (notify) {
405             mMonitor.getServer().deviceChanged(this, CHANGE_CLIENT_LIST);
406         }
407     }
408 
update(int changeMask)409     void update(int changeMask) {
410         mMonitor.getServer().deviceChanged(this, changeMask);
411     }
412 
update(Client client, int changeMask)413     void update(Client client, int changeMask) {
414         mMonitor.getServer().clientChanged(client, changeMask);
415     }
416 
addProperty(String label, String value)417     void addProperty(String label, String value) {
418         mProperties.put(label, value);
419     }
420 
421     /**
422      * {@inheritDoc}
423      */
installPackage(String packageFilePath, boolean reinstall)424     public String installPackage(String packageFilePath, boolean reinstall)
425            throws IOException {
426        String remoteFilePath = syncPackageToDevice(packageFilePath);
427        String result = installRemotePackage(remoteFilePath, reinstall);
428        removeRemotePackage(remoteFilePath);
429        return result;
430     }
431 
432     /**
433      * {@inheritDoc}
434      */
syncPackageToDevice(String localFilePath)435     public String syncPackageToDevice(String localFilePath)
436             throws IOException {
437         try {
438             String packageFileName = getFileName(localFilePath);
439             String remoteFilePath = String.format("/data/local/tmp/%1$s", packageFileName); //$NON-NLS-1$
440 
441             Log.d(packageFileName, String.format("Uploading %1$s onto device '%2$s'",
442                     packageFileName, getSerialNumber()));
443 
444             SyncService sync = getSyncService();
445             if (sync != null) {
446                 String message = String.format("Uploading file onto device '%1$s'",
447                         getSerialNumber());
448                 Log.d(LOG_TAG, message);
449                 SyncResult result = sync.pushFile(localFilePath, remoteFilePath,
450                         SyncService.getNullProgressMonitor());
451 
452                 if (result.getCode() != SyncService.RESULT_OK) {
453                     throw new IOException(String.format("Unable to upload file: %1$s",
454                             result.getMessage()));
455                 }
456             } else {
457                 throw new IOException("Unable to open sync connection!");
458             }
459             return remoteFilePath;
460         } catch (IOException e) {
461             Log.e(LOG_TAG, String.format("Unable to open sync connection! reason: %1$s",
462                     e.getMessage()));
463             throw e;
464         }
465     }
466 
467     /**
468      * Helper method to retrieve the file name given a local file path
469      * @param filePath full directory path to file
470      * @return {@link String} file name
471      */
getFileName(String filePath)472     private String getFileName(String filePath) {
473         return new File(filePath).getName();
474     }
475 
476     /**
477      * {@inheritDoc}
478      */
installRemotePackage(String remoteFilePath, boolean reinstall)479     public String installRemotePackage(String remoteFilePath, boolean reinstall)
480             throws IOException {
481         InstallReceiver receiver = new InstallReceiver();
482         String cmd = String.format(reinstall ? "pm install -r \"%1$s\"" : "pm install \"%1$s\"",
483                             remoteFilePath);
484         executeShellCommand(cmd, receiver);
485         return receiver.getErrorMessage();
486     }
487 
488     /**
489      * {@inheritDoc}
490      */
removeRemotePackage(String remoteFilePath)491     public void removeRemotePackage(String remoteFilePath) throws IOException {
492         // now we delete the app we sync'ed
493         try {
494             executeShellCommand("rm " + remoteFilePath, new NullOutputReceiver());
495         } catch (IOException e) {
496             Log.e(LOG_TAG, String.format("Failed to delete temporary package: %1$s",
497                     e.getMessage()));
498             throw e;
499         }
500     }
501 
502     /**
503      * {@inheritDoc}
504      */
uninstallPackage(String packageName)505     public String uninstallPackage(String packageName) throws IOException {
506         InstallReceiver receiver = new InstallReceiver();
507         executeShellCommand("pm uninstall " + packageName, receiver);
508         return receiver.getErrorMessage();
509     }
510 }
511