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