├── README.md └── src └── adb.php /README.md: -------------------------------------------------------------------------------- 1 | # php-adb 2 | Simple wrapper of Android Debug Bridge for PHP. 3 | -------------------------------------------------------------------------------- /src/adb.php: -------------------------------------------------------------------------------- 1 | bin = self::BIN_WINDOWS; 56 | break; 57 | case "Darwin": 58 | $this -> bin = self::BIN_DARWIN; 59 | break; 60 | default: 61 | $this -> bin = self::BIN_LINUX; 62 | } 63 | 64 | if ($bin_path !== "") { 65 | $sep = PHP_OS_FAMILY === "Windows" ? "\\" : "/"; 66 | if (substr($bin_path, -1) !== $sep) { 67 | $bin_path .= DIRECTORY_SEPARATOR; 68 | } 69 | $this -> bin = "\"" . $bin_path . $this -> bin . "\""; 70 | } 71 | 72 | self::runAdb("root"); 73 | 74 | self::refreshDeviceList(); 75 | } 76 | 77 | /** 78 | * Refresh device list and get it 79 | * 80 | * @access public 81 | * @return array Device list 82 | */ 83 | public function refreshDeviceList() { 84 | $this -> devices = array(); 85 | $result = self::runAdb("devices -l"); 86 | if (self::judgeOutput($result)) { 87 | array_shift($result[0]); // List of devices attached 88 | foreach ($result[0] as $key => $value) { 89 | $value = preg_replace("/[ \t]+/is", " ", $value); 90 | $device = explode(" ", $value); 91 | $temp = array("serial" => "", "status" => "", "transport" => ""); 92 | switch ($device[1]) { 93 | case self::CONNECT_TYPE_DEVICE: 94 | case self::CONNECT_TYPE_RECOVERY: 95 | $transport = str_replace("transport_id:", "", $device[5]); 96 | $temp["manufacturer"] = self::runAdb("-t " . $transport . " shell getprop ro.product.manufacturer")[0][0]; 97 | $temp["brand"] = self::runAdb("-t " . $transport . " shell getprop ro.product.brand")[0][0]; 98 | $temp["board"] = self::runAdb("-t " . $transport . " shell getprop ro.product.board")[0][0]; 99 | $temp["name"] = self::runAdb("-t " . $transport . " shell getprop ro.product.name")[0][0]; 100 | case self::CONNECT_TYPE_SIDELOAD: 101 | case self::CONNECT_TYPE_RESCUE: 102 | $temp["serial"] = $device[0]; 103 | $temp["status"] = $device[1]; 104 | $temp["product"] = str_replace("product:", "", $device[2]); 105 | $temp["model"] = str_replace("model:", "", $device[3]); 106 | $temp["device"] = str_replace("device:", "", $device[4]); 107 | $temp["transport"] = str_replace("transport_id:", "", $device[5]); 108 | break; 109 | case self::CONNECT_TYPE_UNAUTHORIZED: 110 | case self::CONNECT_TYPE_OFFLINE: 111 | $temp["serial"] = $device[0]; 112 | $temp["status"] = $device[1]; 113 | $temp["transport"] = str_replace("transport_id:", "", $device[2]); 114 | break; 115 | } 116 | $this -> devices[] = $temp; 117 | } 118 | } 119 | return $this -> devices; 120 | } 121 | 122 | /** 123 | * Get device list 124 | * 125 | * @access public 126 | * @return array Device list 127 | */ 128 | public function getDeviceList() { 129 | return $this -> devices; 130 | } 131 | 132 | /* TODO: Too lazy to write documents lmao */ 133 | 134 | public function startServer() { 135 | return self::runAdbJudge("start-server"); 136 | } 137 | 138 | public function killServer($force = false) { 139 | if ($force) { 140 | if (PHP_OS_FAMILY !== "Windows") { 141 | echo("Force termination is not implemented on non-Windows systems, fallbacking to normal." . PHP_EOL); 142 | } else { 143 | return self::judgeOutput(self::execShell("taskkill /f /im " . $this -> bin)); 144 | } 145 | } 146 | return self::runAdbJudge("kill-server"); 147 | } 148 | 149 | public function restartServer($force = false) { 150 | self::killServer($force); 151 | return startServer(); 152 | } 153 | 154 | public function sendInput($type = "", $args = "", $device = "") { 155 | return self::runAdb($device . "shell input " . $type . " " . $args); 156 | } 157 | 158 | public function setScreenSize($size = "reset", $device = "") { 159 | return self::runAdbJudge($device . "shell wm size " . $size); 160 | } 161 | 162 | public function getScreenSize($device = "") { 163 | $o = self::runAdb($device . "shell wm size"); 164 | return self::judgeOutput($o) ? array(str_replace("Physical size: ", "", $o[0][0]), isset($output[0][1]) ? str_replace("Override size: ", "", $o[0][1]) : $physical) : false; 165 | } 166 | 167 | public function setScreenDensity($size = "reset", $device = "") { 168 | return self::runAdbJudge($device . "shell wm density " . $size); 169 | } 170 | 171 | public function getScreenDensity($device = "") { 172 | $o = self::runAdb($device . "shell wm density"); 173 | return self::judgeOutput($o) ? array(str_replace("Physical density: ", "", $o[0][0]), isset($o[0][1]) ? str_replace("Override density: ", "", $o[0][1]) : $physical) : false; 174 | } 175 | 176 | public function getScreenshotPNG($device = "") { 177 | $o = self::runAdb($device . "exec-out screencap -p", true); 178 | return self::judgeOutput($o) ? $o[0] : false; 179 | } 180 | 181 | public function getPackage($package, $device = "") { 182 | $o = self::runAdb($device . "shell pm path " . $package); 183 | return self::judgeOutput($o) ? substr($o[0][0], 8) : false; 184 | } 185 | 186 | public function getCurrentActivity($device = "") { 187 | $o = self::runAdb($device . "shell \"dumpsys window | grep mCurrentFocus\""); 188 | if (!self::judgeOutput($o)) { 189 | return array(false, false); 190 | } 191 | if (str_contains($o[0][0], "mCurrentFocus=Window")) { 192 | if (preg_match("/Window\{(.*)\}/", $o[0][0], $matches)) { 193 | $matches = explode(" ", $matches[1]); 194 | $o = explode("/", $matches[count($matches) - 1]); 195 | return array($o[0], isset($o[1]) ? $o[1] : false); 196 | } 197 | } 198 | return array(false, false); 199 | } 200 | 201 | public function getScreenState($device = "") { 202 | $o = self::runAdb($device . "shell \"dumpsys window policy | grep screenState\""); 203 | if (!self::judgeOutput($o)) { 204 | return false; 205 | } 206 | return str_contains($o[0][0], "SCREEN_STATE_ON"); 207 | } 208 | 209 | public function openDocumentUI($path = "", $device = "") { 210 | // Content workaround from https://mlgmxyysd.meowcat.org/2021/02/18/android-r-saf-data/ 211 | return self::runAdbJudge($device . "shell am start -a android.intent.action.VIEW -c android.intent.category.DEFAULT -t vnd.android.document/" . ($path === "" ? "root" : "directory -d content://com.android.externalstorage.documents/tree/primary:" . $path . "/document/primary:" . $path . "") . " com.android.documentsui/.files.FilesActivity"); 212 | } 213 | 214 | public function clearLogcat($device = "") { 215 | return self::runAdb($device . "logcat -c"); 216 | } 217 | 218 | public function runAdb($command, $raw = false) { 219 | return self::execShell($this -> bin . " " . $command, $raw); 220 | } 221 | 222 | public function runAdbJudge($command) { 223 | return self::judgeOutput(self::runAdb($command)); 224 | } 225 | 226 | /* Utilities */ 227 | 228 | public function getDeviceId($device = "", $transport = false) { 229 | return $device === "" ? "" : ($transport ? "-t " : "-s ") . $device . " "; 230 | } 231 | 232 | public function judgeOutput($output, $target = 0) { 233 | return isset($output[1]) && $output[1] === $target ? true : false; 234 | } 235 | 236 | private function execShell($command, $raw = false) { 237 | ob_start(); 238 | passthru($command . " 2>&1", $errorlevel); 239 | $output = ob_get_contents(); 240 | ob_end_clean(); 241 | return array($raw ? $output : explode(PHP_EOL, rtrim($output)), $errorlevel); 242 | } 243 | } 244 | ?> 245 | --------------------------------------------------------------------------------