Installer.java revision 6c4b9de8f1fd594038793c3924b52a44138c319e
1/* 2 * Copyright (C) 2008 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 17package com.android.server.pm; 18 19import android.os.Build; 20import com.android.server.SystemService; 21 22import android.content.Context; 23import android.content.pm.PackageStats; 24import android.net.LocalSocket; 25import android.net.LocalSocketAddress; 26import android.util.Slog; 27import dalvik.system.VMRuntime; 28 29import java.io.IOException; 30import java.io.InputStream; 31import java.io.OutputStream; 32import java.util.List; 33 34public final class Installer extends SystemService { 35 private static final String TAG = "Installer"; 36 37 private static final boolean LOCAL_DEBUG = false; 38 39 InputStream mIn; 40 OutputStream mOut; 41 LocalSocket mSocket; 42 43 byte buf[] = new byte[1024]; 44 int buflen = 0; 45 46 public Installer(Context context) { 47 super(context); 48 } 49 50 @Override 51 public void onStart() { 52 Slog.i(TAG, "Waiting for installd to be ready."); 53 ping(); 54 } 55 56 private boolean connect() { 57 if (mSocket != null) { 58 return true; 59 } 60 Slog.i(TAG, "connecting..."); 61 try { 62 mSocket = new LocalSocket(); 63 64 LocalSocketAddress address = new LocalSocketAddress("installd", 65 LocalSocketAddress.Namespace.RESERVED); 66 67 mSocket.connect(address); 68 69 mIn = mSocket.getInputStream(); 70 mOut = mSocket.getOutputStream(); 71 } catch (IOException ex) { 72 disconnect(); 73 return false; 74 } 75 return true; 76 } 77 78 private void disconnect() { 79 Slog.i(TAG, "disconnecting..."); 80 try { 81 if (mSocket != null) 82 mSocket.close(); 83 } catch (IOException ex) { 84 } 85 try { 86 if (mIn != null) 87 mIn.close(); 88 } catch (IOException ex) { 89 } 90 try { 91 if (mOut != null) 92 mOut.close(); 93 } catch (IOException ex) { 94 } 95 mSocket = null; 96 mIn = null; 97 mOut = null; 98 } 99 100 private boolean readBytes(byte buffer[], int len) { 101 int off = 0, count; 102 if (len < 0) 103 return false; 104 while (off != len) { 105 try { 106 count = mIn.read(buffer, off, len - off); 107 if (count <= 0) { 108 Slog.e(TAG, "read error " + count); 109 break; 110 } 111 off += count; 112 } catch (IOException ex) { 113 Slog.e(TAG, "read exception"); 114 break; 115 } 116 } 117 if (LOCAL_DEBUG) { 118 Slog.i(TAG, "read " + len + " bytes"); 119 } 120 if (off == len) 121 return true; 122 disconnect(); 123 return false; 124 } 125 126 private boolean readReply() { 127 int len; 128 buflen = 0; 129 if (!readBytes(buf, 2)) 130 return false; 131 len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); 132 if ((len < 1) || (len > 1024)) { 133 Slog.e(TAG, "invalid reply length (" + len + ")"); 134 disconnect(); 135 return false; 136 } 137 if (!readBytes(buf, len)) 138 return false; 139 buflen = len; 140 return true; 141 } 142 143 private boolean writeCommand(String _cmd) { 144 byte[] cmd = _cmd.getBytes(); 145 int len = cmd.length; 146 if ((len < 1) || (len > 1024)) 147 return false; 148 buf[0] = (byte) (len & 0xff); 149 buf[1] = (byte) ((len >> 8) & 0xff); 150 try { 151 mOut.write(buf, 0, 2); 152 mOut.write(cmd, 0, len); 153 } catch (IOException ex) { 154 Slog.e(TAG, "write error"); 155 disconnect(); 156 return false; 157 } 158 return true; 159 } 160 161 private synchronized String transaction(String cmd) { 162 if (!connect()) { 163 Slog.e(TAG, "connection failed"); 164 return "-1"; 165 } 166 167 if (!writeCommand(cmd)) { 168 /* 169 * If installd died and restarted in the background (unlikely but 170 * possible) we'll fail on the next write (this one). Try to 171 * reconnect and write the command one more time before giving up. 172 */ 173 Slog.e(TAG, "write command failed? reconnect!"); 174 if (!connect() || !writeCommand(cmd)) { 175 return "-1"; 176 } 177 } 178 if (LOCAL_DEBUG) { 179 Slog.i(TAG, "send: '" + cmd + "'"); 180 } 181 if (readReply()) { 182 String s = new String(buf, 0, buflen); 183 if (LOCAL_DEBUG) { 184 Slog.i(TAG, "recv: '" + s + "'"); 185 } 186 return s; 187 } else { 188 if (LOCAL_DEBUG) { 189 Slog.i(TAG, "fail"); 190 } 191 return "-1"; 192 } 193 } 194 195 private int execute(String cmd) { 196 String res = transaction(cmd); 197 try { 198 return Integer.parseInt(res); 199 } catch (NumberFormatException ex) { 200 return -1; 201 } 202 } 203 204 public int install(String name, int uid, int gid, String seinfo) { 205 StringBuilder builder = new StringBuilder("install"); 206 builder.append(' '); 207 builder.append(name); 208 builder.append(' '); 209 builder.append(uid); 210 builder.append(' '); 211 builder.append(gid); 212 builder.append(' '); 213 builder.append(seinfo != null ? seinfo : "!"); 214 return execute(builder.toString()); 215 } 216 217 public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName, 218 String instructionSet) { 219 if (!isValidInstructionSet(instructionSet)) { 220 Slog.e(TAG, "Invalid instruction set: " + instructionSet); 221 return -1; 222 } 223 224 StringBuilder builder = new StringBuilder("patchoat"); 225 builder.append(' '); 226 builder.append(apkPath); 227 builder.append(' '); 228 builder.append(uid); 229 builder.append(isPublic ? " 1" : " 0"); 230 builder.append(' '); 231 builder.append(pkgName); 232 builder.append(' '); 233 builder.append(instructionSet); 234 return execute(builder.toString()); 235 } 236 237 public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) { 238 if (!isValidInstructionSet(instructionSet)) { 239 Slog.e(TAG, "Invalid instruction set: " + instructionSet); 240 return -1; 241 } 242 243 StringBuilder builder = new StringBuilder("patchoat"); 244 builder.append(' '); 245 builder.append(apkPath); 246 builder.append(' '); 247 builder.append(uid); 248 builder.append(isPublic ? " 1" : " 0"); 249 builder.append(" *"); // No pkgName arg present 250 builder.append(' '); 251 builder.append(instructionSet); 252 return execute(builder.toString()); 253 } 254 255 public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) { 256 if (!isValidInstructionSet(instructionSet)) { 257 Slog.e(TAG, "Invalid instruction set: " + instructionSet); 258 return -1; 259 } 260 261 StringBuilder builder = new StringBuilder("dexopt"); 262 builder.append(' '); 263 builder.append(apkPath); 264 builder.append(' '); 265 builder.append(uid); 266 builder.append(isPublic ? " 1" : " 0"); 267 builder.append(" *"); // No pkgName arg present 268 builder.append(' '); 269 builder.append(instructionSet); 270 return execute(builder.toString()); 271 } 272 273 public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName, 274 String instructionSet) { 275 if (!isValidInstructionSet(instructionSet)) { 276 Slog.e(TAG, "Invalid instruction set: " + instructionSet); 277 return -1; 278 } 279 280 StringBuilder builder = new StringBuilder("dexopt"); 281 builder.append(' '); 282 builder.append(apkPath); 283 builder.append(' '); 284 builder.append(uid); 285 builder.append(isPublic ? " 1" : " 0"); 286 builder.append(' '); 287 builder.append(pkgName); 288 builder.append(' '); 289 builder.append(instructionSet); 290 return execute(builder.toString()); 291 } 292 293 public int idmap(String targetApkPath, String overlayApkPath, int uid) { 294 StringBuilder builder = new StringBuilder("idmap"); 295 builder.append(' '); 296 builder.append(targetApkPath); 297 builder.append(' '); 298 builder.append(overlayApkPath); 299 builder.append(' '); 300 builder.append(uid); 301 return execute(builder.toString()); 302 } 303 304 public int movedex(String srcPath, String dstPath, String instructionSet) { 305 if (!isValidInstructionSet(instructionSet)) { 306 Slog.e(TAG, "Invalid instruction set: " + instructionSet); 307 return -1; 308 } 309 310 StringBuilder builder = new StringBuilder("movedex"); 311 builder.append(' '); 312 builder.append(srcPath); 313 builder.append(' '); 314 builder.append(dstPath); 315 builder.append(' '); 316 builder.append(instructionSet); 317 return execute(builder.toString()); 318 } 319 320 public int rmdex(String codePath, String instructionSet) { 321 if (!isValidInstructionSet(instructionSet)) { 322 Slog.e(TAG, "Invalid instruction set: " + instructionSet); 323 return -1; 324 } 325 326 StringBuilder builder = new StringBuilder("rmdex"); 327 builder.append(' '); 328 builder.append(codePath); 329 builder.append(' '); 330 builder.append(instructionSet); 331 return execute(builder.toString()); 332 } 333 334 public int remove(String name, int userId) { 335 StringBuilder builder = new StringBuilder("remove"); 336 builder.append(' '); 337 builder.append(name); 338 builder.append(' '); 339 builder.append(userId); 340 return execute(builder.toString()); 341 } 342 343 public int rename(String oldname, String newname) { 344 StringBuilder builder = new StringBuilder("rename"); 345 builder.append(' '); 346 builder.append(oldname); 347 builder.append(' '); 348 builder.append(newname); 349 return execute(builder.toString()); 350 } 351 352 public int fixUid(String name, int uid, int gid) { 353 StringBuilder builder = new StringBuilder("fixuid"); 354 builder.append(' '); 355 builder.append(name); 356 builder.append(' '); 357 builder.append(uid); 358 builder.append(' '); 359 builder.append(gid); 360 return execute(builder.toString()); 361 } 362 363 public int deleteCacheFiles(String name, int userId) { 364 StringBuilder builder = new StringBuilder("rmcache"); 365 builder.append(' '); 366 builder.append(name); 367 builder.append(' '); 368 builder.append(userId); 369 return execute(builder.toString()); 370 } 371 372 public int deleteCodeCacheFiles(String name, int userId) { 373 StringBuilder builder = new StringBuilder("rmcodecache"); 374 builder.append(' '); 375 builder.append(name); 376 builder.append(' '); 377 builder.append(userId); 378 return execute(builder.toString()); 379 } 380 381 public int createUserData(String name, int uid, int userId, String seinfo) { 382 StringBuilder builder = new StringBuilder("mkuserdata"); 383 builder.append(' '); 384 builder.append(name); 385 builder.append(' '); 386 builder.append(uid); 387 builder.append(' '); 388 builder.append(userId); 389 builder.append(' '); 390 builder.append(seinfo != null ? seinfo : "!"); 391 return execute(builder.toString()); 392 } 393 394 public int createUserConfig(int userId) { 395 StringBuilder builder = new StringBuilder("mkuserconfig"); 396 builder.append(' '); 397 builder.append(userId); 398 return execute(builder.toString()); 399 } 400 401 public int removeUserDataDirs(int userId) { 402 StringBuilder builder = new StringBuilder("rmuser"); 403 builder.append(' '); 404 builder.append(userId); 405 return execute(builder.toString()); 406 } 407 408 public int clearUserData(String name, int userId) { 409 StringBuilder builder = new StringBuilder("rmuserdata"); 410 builder.append(' '); 411 builder.append(name); 412 builder.append(' '); 413 builder.append(userId); 414 return execute(builder.toString()); 415 } 416 417 public boolean ping() { 418 if (execute("ping") < 0) { 419 return false; 420 } else { 421 return true; 422 } 423 } 424 425 public int pruneDexCache(String cacheSubDir) { 426 return execute("prunedexcache " + cacheSubDir); 427 } 428 429 public int freeCache(long freeStorageSize) { 430 StringBuilder builder = new StringBuilder("freecache"); 431 builder.append(' '); 432 builder.append(String.valueOf(freeStorageSize)); 433 return execute(builder.toString()); 434 } 435 436 public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath, 437 String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) { 438 for (String instructionSet : instructionSets) { 439 if (!isValidInstructionSet(instructionSet)) { 440 Slog.e(TAG, "Invalid instruction set: " + instructionSet); 441 return -1; 442 } 443 } 444 445 StringBuilder builder = new StringBuilder("getsize"); 446 builder.append(' '); 447 builder.append(pkgName); 448 builder.append(' '); 449 builder.append(persona); 450 builder.append(' '); 451 builder.append(apkPath); 452 builder.append(' '); 453 // TODO: Extend getSizeInfo to look at the full subdirectory tree, 454 // not just the first level. 455 builder.append(libDirPath != null ? libDirPath : "!"); 456 builder.append(' '); 457 builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); 458 builder.append(' '); 459 builder.append(asecPath != null ? asecPath : "!"); 460 builder.append(' '); 461 // TODO: Extend getSizeInfo to look at *all* instrution sets, not 462 // just the primary. 463 builder.append(instructionSets[0]); 464 465 String s = transaction(builder.toString()); 466 String res[] = s.split(" "); 467 468 if ((res == null) || (res.length != 5)) { 469 return -1; 470 } 471 try { 472 pStats.codeSize = Long.parseLong(res[1]); 473 pStats.dataSize = Long.parseLong(res[2]); 474 pStats.cacheSize = Long.parseLong(res[3]); 475 pStats.externalCodeSize = Long.parseLong(res[4]); 476 return Integer.parseInt(res[0]); 477 } catch (NumberFormatException e) { 478 return -1; 479 } 480 } 481 482 public int moveFiles() { 483 return execute("movefiles"); 484 } 485 486 /** 487 * Links the 32 bit native library directory in an application's data directory to the 488 * real location for backward compatibility. Note that no such symlink is created for 489 * 64 bit shared libraries. 490 * 491 * @return -1 on error 492 */ 493 public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) { 494 if (dataPath == null) { 495 Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null"); 496 return -1; 497 } else if (nativeLibPath32 == null) { 498 Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null"); 499 return -1; 500 } 501 502 StringBuilder builder = new StringBuilder("linklib "); 503 builder.append(dataPath); 504 builder.append(' '); 505 builder.append(nativeLibPath32); 506 builder.append(' '); 507 builder.append(userId); 508 509 return execute(builder.toString()); 510 } 511 512 public boolean restoreconData(String pkgName, String seinfo, int uid) { 513 StringBuilder builder = new StringBuilder("restorecondata"); 514 builder.append(' '); 515 builder.append(pkgName); 516 builder.append(' '); 517 builder.append(seinfo != null ? seinfo : "!"); 518 builder.append(' '); 519 builder.append(uid); 520 return (execute(builder.toString()) == 0); 521 } 522 523 /** 524 * Returns true iff. {@code instructionSet} is a valid instruction set. 525 */ 526 private static boolean isValidInstructionSet(String instructionSet) { 527 if (instructionSet == null) { 528 return false; 529 } 530 531 for (String abi : Build.SUPPORTED_ABIS) { 532 if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) { 533 return true; 534 } 535 } 536 537 return false; 538 } 539} 540