Installer.java revision 4ed745d359ada6986ac15d8718452e5c55f40170
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 dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) { 216 StringBuilder builder = new StringBuilder("dexopt"); 217 builder.append(' '); 218 builder.append(apkPath); 219 builder.append(' '); 220 builder.append(uid); 221 builder.append(isPublic ? " 1" : " 0"); 222 builder.append(" *"); // No pkgName arg present 223 builder.append(' '); 224 builder.append(instructionSet); 225 return execute(builder.toString()); 226 } 227 228 public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName, 229 String instructionSet) { 230 StringBuilder builder = new StringBuilder("dexopt"); 231 builder.append(' '); 232 builder.append(apkPath); 233 builder.append(' '); 234 builder.append(uid); 235 builder.append(isPublic ? " 1" : " 0"); 236 builder.append(' '); 237 builder.append(pkgName); 238 builder.append(' '); 239 builder.append(instructionSet); 240 return execute(builder.toString()); 241 } 242 243 public int idmap(String targetApkPath, String overlayApkPath, int uid) { 244 StringBuilder builder = new StringBuilder("idmap"); 245 builder.append(' '); 246 builder.append(targetApkPath); 247 builder.append(' '); 248 builder.append(overlayApkPath); 249 builder.append(' '); 250 builder.append(uid); 251 return execute(builder.toString()); 252 } 253 254 public int movedex(String srcPath, String dstPath, String instructionSet) { 255 StringBuilder builder = new StringBuilder("movedex"); 256 builder.append(' '); 257 builder.append(srcPath); 258 builder.append(' '); 259 builder.append(dstPath); 260 builder.append(' '); 261 builder.append(instructionSet); 262 return execute(builder.toString()); 263 } 264 265 public int rmdex(String codePath, String instructionSet) { 266 StringBuilder builder = new StringBuilder("rmdex"); 267 builder.append(' '); 268 builder.append(codePath); 269 builder.append(' '); 270 builder.append(instructionSet); 271 return execute(builder.toString()); 272 } 273 274 public int remove(String name, int userId) { 275 StringBuilder builder = new StringBuilder("remove"); 276 builder.append(' '); 277 builder.append(name); 278 builder.append(' '); 279 builder.append(userId); 280 return execute(builder.toString()); 281 } 282 283 public int rename(String oldname, String newname) { 284 StringBuilder builder = new StringBuilder("rename"); 285 builder.append(' '); 286 builder.append(oldname); 287 builder.append(' '); 288 builder.append(newname); 289 return execute(builder.toString()); 290 } 291 292 public int fixUid(String name, int uid, int gid) { 293 StringBuilder builder = new StringBuilder("fixuid"); 294 builder.append(' '); 295 builder.append(name); 296 builder.append(' '); 297 builder.append(uid); 298 builder.append(' '); 299 builder.append(gid); 300 return execute(builder.toString()); 301 } 302 303 public int deleteCacheFiles(String name, int userId) { 304 StringBuilder builder = new StringBuilder("rmcache"); 305 builder.append(' '); 306 builder.append(name); 307 builder.append(' '); 308 builder.append(userId); 309 return execute(builder.toString()); 310 } 311 312 public int deleteCodeCacheFiles(String name, int userId) { 313 StringBuilder builder = new StringBuilder("rmcodecache"); 314 builder.append(' '); 315 builder.append(name); 316 builder.append(' '); 317 builder.append(userId); 318 return execute(builder.toString()); 319 } 320 321 public int createUserData(String name, int uid, int userId, String seinfo) { 322 StringBuilder builder = new StringBuilder("mkuserdata"); 323 builder.append(' '); 324 builder.append(name); 325 builder.append(' '); 326 builder.append(uid); 327 builder.append(' '); 328 builder.append(userId); 329 builder.append(' '); 330 builder.append(seinfo != null ? seinfo : "!"); 331 return execute(builder.toString()); 332 } 333 334 public int createUserConfig(int userId) { 335 StringBuilder builder = new StringBuilder("mkuserconfig"); 336 builder.append(' '); 337 builder.append(userId); 338 return execute(builder.toString()); 339 } 340 341 public int removeUserDataDirs(int userId) { 342 StringBuilder builder = new StringBuilder("rmuser"); 343 builder.append(' '); 344 builder.append(userId); 345 return execute(builder.toString()); 346 } 347 348 public int clearUserData(String name, int userId) { 349 StringBuilder builder = new StringBuilder("rmuserdata"); 350 builder.append(' '); 351 builder.append(name); 352 builder.append(' '); 353 builder.append(userId); 354 return execute(builder.toString()); 355 } 356 357 public boolean ping() { 358 if (execute("ping") < 0) { 359 return false; 360 } else { 361 return true; 362 } 363 } 364 365 public int pruneDexCache(String cacheSubDir) { 366 return execute("prunedexcache " + cacheSubDir); 367 } 368 369 public int freeCache(long freeStorageSize) { 370 StringBuilder builder = new StringBuilder("freecache"); 371 builder.append(' '); 372 builder.append(String.valueOf(freeStorageSize)); 373 return execute(builder.toString()); 374 } 375 376 public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath, 377 String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) { 378 StringBuilder builder = new StringBuilder("getsize"); 379 builder.append(' '); 380 builder.append(pkgName); 381 builder.append(' '); 382 builder.append(persona); 383 builder.append(' '); 384 builder.append(apkPath); 385 builder.append(' '); 386 // TODO: Extend getSizeInfo to look at the full subdirectory tree, 387 // not just the first level. 388 builder.append(libDirPath != null ? libDirPath : "!"); 389 builder.append(' '); 390 builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); 391 builder.append(' '); 392 builder.append(asecPath != null ? asecPath : "!"); 393 builder.append(' '); 394 // TODO: Extend getSizeInfo to look at *all* instrution sets, not 395 // just the primary. 396 builder.append(instructionSets[0]); 397 398 String s = transaction(builder.toString()); 399 String res[] = s.split(" "); 400 401 if ((res == null) || (res.length != 5)) { 402 return -1; 403 } 404 try { 405 pStats.codeSize = Long.parseLong(res[1]); 406 pStats.dataSize = Long.parseLong(res[2]); 407 pStats.cacheSize = Long.parseLong(res[3]); 408 pStats.externalCodeSize = Long.parseLong(res[4]); 409 return Integer.parseInt(res[0]); 410 } catch (NumberFormatException e) { 411 return -1; 412 } 413 } 414 415 public int moveFiles() { 416 return execute("movefiles"); 417 } 418 419 /** 420 * Links the 32 bit native library directory in an application's data directory to the 421 * real location for backward compatibility. Note that no such symlink is created for 422 * 64 bit shared libraries. 423 * 424 * @return -1 on error 425 */ 426 public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) { 427 if (dataPath == null) { 428 Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null"); 429 return -1; 430 } else if (nativeLibPath32 == null) { 431 Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null"); 432 return -1; 433 } 434 435 StringBuilder builder = new StringBuilder("linklib "); 436 builder.append(dataPath); 437 builder.append(' '); 438 builder.append(nativeLibPath32); 439 builder.append(' '); 440 builder.append(userId); 441 442 return execute(builder.toString()); 443 } 444 445 public boolean restoreconData(String pkgName, String seinfo, int uid) { 446 StringBuilder builder = new StringBuilder("restorecondata"); 447 builder.append(' '); 448 builder.append(pkgName); 449 builder.append(' '); 450 builder.append(seinfo != null ? seinfo : "!"); 451 builder.append(' '); 452 builder.append(uid); 453 return (execute(builder.toString()) == 0); 454 } 455} 456