]> code.delx.au - gnu-emacs/blobdiff - lisp/net/tramp-adb.el
* net/tramp-adb.el (tramp-adb-prompt): Extend regexp.
[gnu-emacs] / lisp / net / tramp-adb.el
index 79de7ab07bbd34062470d699774165dd82dcf9ee..910356fbb6c1365937796b0d33bdb2e10679f467 100644 (file)
@@ -1,6 +1,6 @@
 ;;; tramp-adb.el --- Functions for calling Android Debug Bridge from Tramp
 
-;; Copyright (C) 2011-2012 Free Software Foundation, Inc.
+;; Copyright (C) 2011-2013 Free Software Foundation, Inc.
 
 ;; Author: Juergen Hoetzel <juergen@archlinux.org>
 ;; Keywords: comm, processes
 
 ;;; Commentary:
 
-;; The Android Debug Bridge must be installed on your local machine.
-;; Add the following form into your .emacs:
+;; The Android Debug Bridge "adb" must be installed on your local
+;; machine.  If it is not in your $PATH, add the following form into
+;; your .emacs:
 ;;
-;;   (setq tramp-adb-sdk-dir "/path/to/android/sdk")
+;;   (setq tramp-adb-program "/path/to/adb")
 ;;
 ;; Due to security it is not possible to access non-root devices.
 
 ;;; Code:
 
 (require 'tramp)
+(require 'time-date)
 
 (defvar dired-move-to-filename-regexp)
 
-(defcustom tramp-adb-sdk-dir "~/Android/sdk"
-  "Set to the directory containing the Android SDK."
-  :type 'string
+(defcustom tramp-adb-program "adb"
+  "Name of the Android Debug Bridge program."
+  :group 'tramp
   :version "24.4"
-  :group 'tramp)
+  :type 'string)
 
 ;;;###tramp-autoload
 (defconst tramp-adb-method "adb"
   "*When this method name is used, forward all calls to Android Debug Bridge.")
 
 (defcustom tramp-adb-prompt
-  "^\\(?:[[:alnum:]]*@[[:alnum:]]*[^#\\$]*\\)?[#\\$][[:space:]]"
+  "^\\(?:[[:digit:]]*|?\\)?\\(?:[[:alnum:]]*@[[:alnum:]]*[^#\\$]*\\)?[#\\$][[:space:]]"
   "Regexp used as prompt in almquist shell."
   :type 'string
   :version "24.4"
 (defconst tramp-adb-ls-date-regexp
   "[[:space:]][0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9][[:space:]][0-9][0-9]:[0-9][0-9][[:space:]]")
 
+(defconst tramp-adb-ls-toolbox-regexp
+  (concat
+   "^[[:space:]]*\\([-[:alpha:]]+\\)"  ; \1 permissions
+   "[[:space:]]*\\([^[:space:]]+\\)"   ; \2 username
+   "[[:space:]]+\\([^[:space:]]+\\)"   ; \3 group
+   "[[:space:]]+\\([[:digit:]]+\\)"    ; \4 size
+   "[[:space:]]+\\([-[:digit:]]+[[:space:]][:[:digit:]]+\\)" ; \5 date
+   "[[:space:]]+\\(.*\\)$"))           ; \6 filename
+
 ;;;###tramp-autoload
-(add-to-list 'tramp-methods `(,tramp-adb-method))
+(add-to-list 'tramp-methods
+            `(,tramp-adb-method
+              (tramp-tmpdir "/data/local/tmp")))
+
+;;;###tramp-autoload
+(add-to-list 'tramp-default-host-alist `(,tramp-adb-method nil ""))
 
 ;;;###tramp-autoload
 (eval-after-load 'tramp
     (file-name-as-directory . tramp-handle-file-name-as-directory)
     (file-regular-p . tramp-handle-file-regular-p)
     (file-remote-p . tramp-handle-file-remote-p)
+    (file-accessible-directory-p . tramp-handle-file-accessible-directory-p)
     (file-directory-p . tramp-adb-handle-file-directory-p)
     (file-symlink-p . tramp-handle-file-symlink-p)
     ;; FIXME: This is too sloppy.
-    (file-executable-p . file-exists-p)
-    (file-exists-p . tramp-adb-handle-file-exists-p)
+    (file-executable-p . tramp-handle-file-exists-p)
+    (file-exists-p . tramp-handle-file-exists-p)
     (file-readable-p . tramp-handle-file-exists-p)
     (file-writable-p . tramp-adb-handle-file-writable-p)
     (file-local-copy . tramp-adb-handle-file-local-copy)
     (expand-file-name . tramp-adb-handle-expand-file-name)
     (find-backup-file-name . tramp-handle-find-backup-file-name)
     (directory-files . tramp-handle-directory-files)
+    (directory-files-and-attributes
+     . tramp-adb-handle-directory-files-and-attributes)
     (make-directory . tramp-adb-handle-make-directory)
     (delete-directory . tramp-adb-handle-delete-directory)
     (delete-file . tramp-adb-handle-delete-file)
     (vc-registered . ignore)   ;no  vc control files on Android devices
     (write-region . tramp-adb-handle-write-region)
     (set-file-modes . tramp-adb-handle-set-file-modes)
-    (set-file-times . ignore)
+    (set-file-times . tramp-adb-handle-set-file-times)
     (copy-file . tramp-adb-handle-copy-file)
     (rename-file . tramp-adb-handle-rename-file)
     (process-file . tramp-adb-handle-process-file)
   "Invoke the ADB handler for OPERATION.
 First arg specifies the OPERATION, second arg is a list of arguments to
 pass to the OPERATION."
-  (let ((fn (assoc operation tramp-adb-file-name-handler-alist))
-       ;; `tramp-default-host's default value is (system-name).  Not
-       ;; useful for us.
-       (tramp-default-host
-        (unless (equal (eval (car (get 'tramp-default-host 'standard-value)))
-                       tramp-default-host)
-          tramp-default-host)))
+  (let ((fn (assoc operation tramp-adb-file-name-handler-alist)))
     (if fn
        (save-match-data (apply (cdr fn) args))
       (tramp-run-real-handler operation args))))
 
-;; This cannot be a constant, because `tramp-adb-sdk-dir' is customizable.
-(defun tramp-adb-program ()
-  "The Android Debug Bridge."
-  (expand-file-name "platform-tools/adb" tramp-adb-sdk-dir))
-
 ;;;###tramp-autoload
 (defun tramp-adb-parse-device-names (ignore)
   "Return a list of (nil host) tuples allowed to access."
-  (with-temp-buffer
-    (when (zerop (call-process (tramp-adb-program) nil t nil "devices"))
-      (let (result)
-       (goto-char (point-min))
-       (while (search-forward-regexp "^\\(\\S-+\\)[[:space:]]+device$" nil t)
-         (add-to-list 'result (list nil (match-string 1))))
-       result))))
+  (with-timeout (10)
+    (with-temp-buffer
+      (when (zerop (call-process tramp-adb-program nil t nil "devices"))
+       (let (result)
+         (goto-char (point-min))
+         (while (search-forward-regexp "^\\(\\S-+\\)[[:space:]]+device$" nil t)
+           (add-to-list 'result (list nil (match-string 1))))
+         result)))))
 
 (defun tramp-adb-handle-expand-file-name (name &optional dir)
   "Like `expand-file-name' for Tramp files."
@@ -279,30 +288,75 @@ pass to the OPERATION."
                   (tramp-shell-quote-argument localname)) "")
        (with-current-buffer (tramp-get-buffer v)
          (tramp-adb-sh-fix-ls-output)
-         (let* ((columns (split-string (buffer-string)))
-                (mod-string (nth 0 columns))
-                (is-dir (eq ?d (aref mod-string 0)))
-                (is-symlink (eq ?l (aref mod-string 0)))
-                (symlink-target
-                 (and is-symlink
-                      (cadr (split-string (buffer-string) "\\( -> \\|\n\\)"))))
-                (uid (nth 1 columns))
-                (gid (nth 2 columns))
-                (date (format "%s %s" (nth 4 columns) (nth 5 columns)))
-                (size (string-to-number (nth 3 columns))))
-           (list
-            (or is-dir symlink-target)
-            1                                  ;link-count
-            ;; no way to handle numeric ids in Androids ash
-            (if (eq id-format 'integer) 0 uid)
-            (if (eq id-format 'integer) 0 gid)
-            '(0 0) ; atime
-            (date-to-time date) ; mtime
-            '(0 0) ; ctime
-            size
-            mod-string
-            ;; fake
-            t 1 1)))))))
+         (cdar (tramp-do-parse-file-attributes-with-ls v id-format)))))))
+
+(defun tramp-do-parse-file-attributes-with-ls (vec &optional id-format)
+  "Parse `file-attributes' for Tramp files using the ls(1) command."
+  (with-current-buffer (tramp-get-buffer vec)
+    (goto-char (point-min))
+    (let ((file-properties nil))
+      (while (re-search-forward tramp-adb-ls-toolbox-regexp nil t)
+       (let* ((mod-string (match-string 1))
+              (is-dir (eq ?d (aref mod-string 0)))
+              (is-symlink (eq ?l (aref mod-string 0)))
+              (uid (match-string 2))
+              (gid (match-string 3))
+              (size (string-to-number (match-string 4)))
+              (date (match-string 5))
+              (name (match-string 6))
+              (symlink-target
+               (and is-symlink
+                    (cadr (split-string name "\\( -> \\|\n\\)")))))
+         (push (list
+                (if is-symlink
+                    (car (split-string name "\\( -> \\|\n\\)"))
+                  name)
+                (or is-dir symlink-target)
+                1     ;link-count
+                ;; no way to handle numeric ids in Androids ash
+                (if (eq id-format 'integer) 0 uid)
+                (if (eq id-format 'integer) 0 gid)
+                '(0 0)                 ; atime
+                (date-to-time date)    ; mtime
+                '(0 0)                 ; ctime
+                size
+                mod-string
+                ;; fake
+                t 1
+                (tramp-get-device vec))
+               file-properties)))
+      file-properties)))
+
+(defun tramp-adb-handle-directory-files-and-attributes
+  (directory &optional full match nosort id-format)
+  "Like `directory-files-and-attributes' for Tramp files."
+  (when (file-directory-p directory)
+    (with-parsed-tramp-file-name (expand-file-name directory) nil
+      (with-tramp-file-property
+         v localname (format "directory-files-attributes-%s-%s-%s-%s"
+                             full match id-format nosort)
+       (tramp-adb-barf-unless-okay
+        v (format "%s -a -l %s"
+                  (tramp-adb-get-ls-command v)
+                  (tramp-shell-quote-argument localname)) "")
+       (with-current-buffer (tramp-get-buffer v)
+         (tramp-adb-sh-fix-ls-output)
+         (let ((result (tramp-do-parse-file-attributes-with-ls
+                        v (or id-format 'integer))))
+           (when full
+             (setq result
+                   (mapcar
+                    (lambda (x)
+                      (cons (expand-file-name (car x) directory) (cdr x)))
+                    result)))
+           (unless nosort
+             (setq result
+                   (sort result (lambda (x y) (string< (car x) (car y))))))
+           (delq nil
+                 (mapcar (lambda (x)
+                           (if (or (not match) (string-match match (car x)))
+                               x))
+                         result))))))))
 
 (defun tramp-adb-get-ls-command (vec)
   (with-tramp-connection-property vec "ls"
@@ -316,14 +370,14 @@ pass to the OPERATION."
       "ls")))
 
 (defun tramp-adb-get-toolbox (vec)
-  "Get shell toolbox implementation: `toolbox' for orginal distributions
-or `busybox' for CynagenMode based distributions"
+  "Get shell toolbox implementation: `toolbox' for original distributions
+or `busybox' for CyanogenMod based distributions"
   (with-tramp-connection-property vec "toolbox"
     (tramp-message vec 5 "Checking shell toolbox implementation")
     (cond
      ((zerop (tramp-adb-command-exit-status vec "busybox")) 'busybox)
      ((zerop (tramp-adb-command-exit-status vec "toolbox")) 'toolbox)
-     (t 'unkown))))
+     (t 'unknown))))
 
 (defun tramp-adb--gnu-switches-to-ash
   (switches)
@@ -409,7 +463,7 @@ Emacs dired can't find files."
     (setq time-a (apply 'encode-time (parse-time-string (match-string 0 a))))
     (string-match tramp-adb-ls-date-regexp b)
     (setq time-b (apply 'encode-time (parse-time-string (match-string 0 b))))
-    (time-less-p time-b time-a)))
+    (tramp-time-less-p time-b time-a)))
 
 (defun tramp-adb-ls-output-name-less-p (a b)
   "Sort \"ls\" output by name, ascending."
@@ -560,6 +614,19 @@ But handle the case, if the \"test\" command is not available."
      v (format "chmod %s %s" (tramp-compat-decimal-to-octal mode) localname)
      "Error while changing file's mode %s" filename)))
 
+(defun tramp-adb-handle-set-file-times (filename &optional time)
+  "Like `set-file-times' for Tramp files."
+  (with-parsed-tramp-file-name filename nil
+    (tramp-flush-file-property v localname)
+    (let ((time (if (or (null time) (equal time '(0 0)))
+                   (current-time)
+                 time)))
+      (tramp-adb-command-exit-status
+       ;; use shell arithmetic because of Emacs integer size limit
+       v (format "touch -t $(( %d * 65536 + %d )) %s"
+                (car time) (cadr time)
+                (tramp-shell-quote-argument localname))))))
+
 (defun tramp-adb-handle-copy-file
   (filename newname &optional ok-if-already-exists keep-date
            preserve-uid-gid preserve-extended-attributes)
@@ -569,7 +636,7 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
        newname (expand-file-name newname))
 
   (if (file-directory-p filename)
-      (copy-directory filename newname keep-date t)
+      (tramp-file-name-handler 'copy-directory filename newname keep-date t)
     (with-tramp-progress-reporter
        (tramp-dissect-file-name (if (file-remote-p filename) filename newname))
        0 (format "Copying %s to %s" filename newname)
@@ -629,7 +696,10 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
            (tramp-flush-file-property v localname)
            ;; Short track.
            (tramp-adb-barf-unless-okay
-            v (format "mv %s %s" (file-remote-p filename 'localname) localname)
+            v (format
+               "mv %s %s"
+               (tramp-file-name-handler 'file-remote-p filename 'localname)
+               localname)
             "Error renaming %s to %s" filename newname))
 
        ;; Rename by copy.
@@ -705,13 +775,11 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
       ;; directory.
       (condition-case nil
          (progn
-           (setq ret 0
-                 ret
-                 (tramp-adb-barf-unless-okay
-                  v (format "(cd %s; %s)"
-                            (tramp-shell-quote-argument localname)
-                            command)
-                  ""))
+           (setq ret 0)
+           (tramp-adb-barf-unless-okay
+            v (format "(cd %s; %s)"
+                      (tramp-shell-quote-argument localname) command)
+            "")
            ;; We should show the output anyway.
            (when outbuf
              (with-current-buffer outbuf
@@ -829,82 +897,90 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
 (defun tramp-adb-handle-start-file-process (name buffer program &rest args)
   "Like `start-file-process' for Tramp files."
   (with-parsed-tramp-file-name default-directory nil
-    ;; When PROGRAM is nil, we just provide a tty.
+    ;; When PROGRAM is nil, we should provide a tty.  This is not
+    ;; possible here.
+    (unless (stringp program)
+      (tramp-error v 'file-error "PROGRAM must be a string"))
+
     (let ((command
-          (when (stringp program)
-            (format "cd %s; %s"
-                    (tramp-shell-quote-argument localname)
-                    (mapconcat 'tramp-shell-quote-argument
-                               (cons program args) " "))))
+          (format "cd %s; %s"
+                  (tramp-shell-quote-argument localname)
+                  (mapconcat 'tramp-shell-quote-argument
+                             (cons program args) " ")))
          (tramp-process-connection-type
           (or (null program) tramp-process-connection-type))
          (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer)))
          (name1 name)
          (i 0))
-      (unwind-protect
-         (save-excursion
-           (save-restriction
-             (unless buffer
-               ;; BUFFER can be nil.  We use a temporary buffer.
-               (setq buffer (generate-new-buffer tramp-temp-buffer-name)))
-             (while (get-process name1)
-               ;; NAME must be unique as process name.
-               (setq i (1+ i)
-                     name1 (format "%s<%d>" name i)))
-             (setq name name1)
-             ;; Set the new process properties.
-             (tramp-set-connection-property v "process-name" name)
-             (tramp-set-connection-property v "process-buffer" buffer)
-             ;; Activate narrowing in order to save BUFFER contents.
-             ;; Clear also the modification time; otherwise we might
-             ;; be interrupted by `verify-visited-file-modtime'.
-             (with-current-buffer (tramp-get-connection-buffer v)
-               (let ((buffer-undo-list t))
+
+      (unless buffer
+       ;; BUFFER can be nil.  We use a temporary buffer.
+       (setq buffer (generate-new-buffer tramp-temp-buffer-name)))
+      (while (get-process name1)
+       ;; NAME must be unique as process name.
+       (setq i (1+ i)
+             name1 (format "%s<%d>" name i)))
+      (setq name name1)
+      ;; Set the new process properties.
+      (tramp-set-connection-property v "process-name" name)
+      (tramp-set-connection-property v "process-buffer" buffer)
+
+      (with-current-buffer (tramp-get-connection-buffer v)
+       (unwind-protect
+           ;; We catch this event.  Otherwise, `start-process' could
+           ;; be called on the local host.
+           (save-excursion
+             (save-restriction
+               ;; Activate narrowing in order to save BUFFER
+               ;; contents.  Clear also the modification time;
+               ;; otherwise we might be interrupted by
+               ;; `verify-visited-file-modtime'.
+               (let ((buffer-undo-list t)
+                     (buffer-read-only nil)
+                     (mark (point)))
                  (clear-visited-file-modtime)
                  (narrow-to-region (point-max) (point-max))
-                 (if command
-                     ;; Send the command.
-                     (tramp-adb-send-command v command)
-                   ;; Open the connection.
-                   (tramp-adb-maybe-open-connection v))))
-             (let ((p (tramp-get-connection-process v)))
-               ;; Set sentinel and query flag for this process.
-               (tramp-set-connection-property p "vector" v)
-               (set-process-sentinel p 'tramp-process-sentinel)
-               (tramp-compat-set-process-query-on-exit-flag p t)
-               ;; Return process.
-               p)))
-       ;; Save exit.
-       (with-current-buffer (tramp-get-connection-buffer v)
+                 ;; We call `tramp-adb-maybe-open-connection', in
+                 ;; order to cleanup the prompt afterwards.
+                 (tramp-adb-maybe-open-connection v)
+                 (widen)
+                 (delete-region mark (point))
+                 (narrow-to-region (point-max) (point-max))
+                 ;; Send the command.
+                 (let ((tramp-adb-prompt (regexp-quote command)))
+                   (tramp-adb-send-command v command))
+                 (let ((p (tramp-get-connection-process v)))
+                   ;; Set query flag and process marker for this
+                   ;; process.  We ignore errors, because the process
+                   ;; could have finished already.
+                   (ignore-errors
+                     (tramp-compat-set-process-query-on-exit-flag p t)
+                     (set-marker (process-mark p) (point)))
+                   ;; Return process.
+                   p))))
+
+         ;; Save exit.
          (if (string-match tramp-temp-buffer-name (buffer-name))
-             (progn
+             (ignore-errors
                (set-process-buffer (tramp-get-connection-process v) nil)
                (kill-buffer (current-buffer)))
-           (set-buffer-modified-p bmp)))
-       (tramp-set-connection-property v "process-name" nil)
-       (tramp-set-connection-property v "process-buffer" nil)))))
-
-;; Android < 4 doesn't provide test command.
-
-(defun tramp-adb-handle-file-exists-p (filename)
-  "Like `file-exists-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
-    (with-tramp-file-property v localname "file-exists-p"
-      (file-attributes filename))))
+           (set-buffer-modified-p bmp))
+         (tramp-set-connection-property v "process-name" nil)
+         (tramp-set-connection-property v "process-buffer" nil))))))
 
 ;; Helper functions.
 
 (defun tramp-adb-execute-adb-command (vec &rest args)
   "Returns nil on success error-output on failure."
-  (when (tramp-file-name-host vec)
+  (when (> (length (tramp-file-name-host vec)) 0)
     (setq args (append (list "-s" (tramp-file-name-host vec)) args)))
   (with-temp-buffer
     (prog1
-       (unless (zerop (apply 'call-process (tramp-adb-program) nil t nil args))
+       (unless (zerop (apply 'call-process tramp-adb-program nil t nil args))
          (buffer-string))
       (tramp-message
        vec 6 "%s %s\n%s"
-       (tramp-adb-program) (mapconcat 'identity args " ") (buffer-string)))))
+       tramp-adb-program (mapconcat 'identity args " ") (buffer-string)))))
 
 (defun tramp-adb-find-test-command (vec)
   "Checks, whether the ash has a builtin \"test\" command.
@@ -933,34 +1009,31 @@ This happens for Android >= 4.0."
       (while (re-search-forward "\r+$" nil t)
        (replace-match "" nil nil)))))
 
-(defun tramp-adb-barf-unless-okay (vec command fmt &rest args)
-  "Run COMMAND, check exit status, throw error if exit status not okay.
-FMT and ARGS are passed to `error'."
-  (tramp-adb-send-command vec (format "%s; echo tramp_exit_status $?" command))
-  (with-current-buffer (tramp-get-connection-buffer vec)
-    (goto-char (point-max))
-    (unless (re-search-backward "tramp_exit_status [0-9]+" nil t)
-      (tramp-error
-       vec 'file-error "Couldn't find exit status of `%s'" command))
-    (skip-chars-forward "^ ")
-    (unless (zerop (read (current-buffer)))
-      (apply 'tramp-error vec 'file-error fmt args))
-    (let (buffer-read-only)
-      (delete-region (match-beginning 0) (point-max)))))
-
 (defun tramp-adb-command-exit-status
   (vec command)
   "Run COMMAND and return its exit status.
 Sends `echo $?' along with the COMMAND for checking the exit status.  If
 COMMAND is nil, just sends `echo $?'.  Returns the exit status found."
-  (tramp-adb-send-command vec (format "%s; echo tramp_exit_status $?" command))
+  (tramp-adb-send-command
+   vec (if command
+          (format "%s; echo tramp_exit_status $?" command)
+        "echo tramp_exit_status $?"))
   (with-current-buffer (tramp-get-connection-buffer vec)
     (goto-char (point-max))
     (unless (re-search-backward "tramp_exit_status [0-9]+" nil t)
       (tramp-error
        vec 'file-error "Couldn't find exit status of `%s'" command))
     (skip-chars-forward "^ ")
-    (read (current-buffer))))
+    (prog1
+       (read (current-buffer))
+      (let (buffer-read-only)
+       (delete-region (match-beginning 0) (point-max))))))
+
+(defun tramp-adb-barf-unless-okay (vec command fmt &rest args)
+  "Run COMMAND, check exit status, throw error if exit status not okay.
+FMT and ARGS are passed to `error'."
+  (unless (zerop (tramp-adb-command-exit-status vec command))
+    (apply 'tramp-error vec 'file-error fmt args)))
 
 (defun tramp-adb-wait-for-output (proc &optional timeout)
   "Wait for output from remote command."
@@ -997,28 +1070,39 @@ COMMAND is nil, just sends `echo $?'.  Returns the exit status found."
 Does not do anything if a connection is already open, but re-opens the
 connection if a previous connection has died for some reason."
   (let* ((buf (tramp-get-connection-buffer vec))
-        (p (get-buffer-process buf)))
+        (p (get-buffer-process buf))
+        (host (tramp-file-name-host vec))
+        (user (tramp-file-name-user vec))
+        (devices (mapcar 'cadr (tramp-adb-parse-device-names nil))))
     (unless
        (and p (processp p) (memq (process-status p) '(run open)))
       (save-match-data
        (when (and p (processp p)) (delete-process p))
+       (if (not devices)
+           (tramp-error vec 'file-error "No device connected"))
+       (if (and (> (length host) 0) (not (member host devices)))
+           (tramp-error vec 'file-error "Device %s not connected" host))
+       (if (and (> (length devices) 1) (zerop (length host)))
+           (tramp-error
+            vec 'file-error
+            "Multiple Devices connected: No Host/Device specified"))
        (with-tramp-progress-reporter vec 3 "Opening adb shell connection"
          (let* ((coding-system-for-read 'utf-8-dos) ;is this correct?
                 (process-connection-type tramp-process-connection-type)
-                (args (if (tramp-file-name-host vec)
-                          (list "-s" (tramp-file-name-host vec) "shell")
+                (args (if (> (length host) 0)
+                          (list "-s" host "shell")
                         (list "shell")))
                 (p (let ((default-directory
                            (tramp-compat-temporary-file-directory)))
                      (apply 'start-process (tramp-get-connection-name vec) buf
-                            (tramp-adb-program) args))))
+                            tramp-adb-program args))))
            (tramp-message
             vec 6 "%s" (mapconcat 'identity (process-command p) " "))
            ;; Wait for initial prompt.
-           (tramp-adb-wait-for-output p)
+           (tramp-adb-wait-for-output p 30)
            (unless (eq 'run (process-status p))
              (tramp-error  vec 'file-error "Terminated!"))
-           (set-process-query-on-exit-flag p nil)
+           (tramp-compat-set-process-query-on-exit-flag p nil)
 
            ;; Check whether the properties have been changed.  If
            ;; yes, this is a strong indication that we must expire all
@@ -1042,7 +1126,26 @@ connection if a previous connection has died for some reason."
                 vec 3
                 "Connection reset, because remote host changed from `%s' to `%s'"
                 old-getprop new-getprop)
-               (tramp-adb-maybe-open-connection vec)))))))))
+               (tramp-adb-maybe-open-connection vec)))
+
+           ;; Change user if indicated.
+           (when user
+             (tramp-adb-send-command vec (format "su %s" user))
+             (unless (zerop (tramp-adb-command-exit-status vec nil))
+               (delete-process p)
+               (tramp-error vec 'file-error "Cannot switch to user %s" user)))
+
+           ;; Set "remote-path" connection property.  This is needed
+           ;; for eshell.
+           (tramp-adb-send-command vec "echo \\\"$PATH\\\"")
+           (tramp-set-connection-property
+            vec "remote-path"
+            (split-string
+             (with-current-buffer (tramp-get-connection-buffer vec)
+               ;; Read the expression.
+               (goto-char (point-min))
+               (read (current-buffer)))
+             ":" 'omit-nulls))))))))
 
 (provide 'tramp-adb)
 ;;; tramp-adb.el ends here