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