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 17 package com.android.server.pm; 18 19 import android.content.pm.PackageStats; 20 import android.net.LocalSocket; 21 import android.net.LocalSocketAddress; 22 import android.util.Slog; 23 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.io.OutputStream; 27 28 class Installer { 29 private static final String TAG = "Installer"; 30 31 private static final boolean LOCAL_DEBUG = false; 32 33 InputStream mIn; 34 35 OutputStream mOut; 36 37 LocalSocket mSocket; 38 39 byte buf[] = new byte[1024]; 40 41 int buflen = 0; 42 43 private boolean connect() { 44 if (mSocket != null) { 45 return true; 46 } 47 Slog.i(TAG, "connecting..."); 48 try { 49 mSocket = new LocalSocket(); 50 51 LocalSocketAddress address = new LocalSocketAddress("installd", 52 LocalSocketAddress.Namespace.RESERVED); 53 54 mSocket.connect(address); 55 56 mIn = mSocket.getInputStream(); 57 mOut = mSocket.getOutputStream(); 58 } catch (IOException ex) { 59 disconnect(); 60 return false; 61 } 62 return true; 63 } 64 65 private void disconnect() { 66 Slog.i(TAG, "disconnecting..."); 67 try { 68 if (mSocket != null) 69 mSocket.close(); 70 } catch (IOException ex) { 71 } 72 try { 73 if (mIn != null) 74 mIn.close(); 75 } catch (IOException ex) { 76 } 77 try { 78 if (mOut != null) 79 mOut.close(); 80 } catch (IOException ex) { 81 } 82 mSocket = null; 83 mIn = null; 84 mOut = null; 85 } 86 87 private boolean readBytes(byte buffer[], int len) { 88 int off = 0, count; 89 if (len < 0) 90 return false; 91 while (off != len) { 92 try { 93 count = mIn.read(buffer, off, len - off); 94 if (count <= 0) { 95 Slog.e(TAG, "read error " + count); 96 break; 97 } 98 off += count; 99 } catch (IOException ex) { 100 Slog.e(TAG, "read exception"); 101 break; 102 } 103 } 104 if (LOCAL_DEBUG) { 105 Slog.i(TAG, "read " + len + " bytes"); 106 } 107 if (off == len) 108 return true; 109 disconnect(); 110 return false; 111 } 112 113 private boolean readReply() { 114 int len; 115 buflen = 0; 116 if (!readBytes(buf, 2)) 117 return false; 118 len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); 119 if ((len < 1) || (len > 1024)) { 120 Slog.e(TAG, "invalid reply length (" + len + ")"); 121 disconnect(); 122 return false; 123 } 124 if (!readBytes(buf, len)) 125 return false; 126 buflen = len; 127 return true; 128 } 129 130 private boolean writeCommand(String _cmd) { 131 byte[] cmd = _cmd.getBytes(); 132 int len = cmd.length; 133 if ((len < 1) || (len > 1024)) 134 return false; 135 buf[0] = (byte) (len & 0xff); 136 buf[1] = (byte) ((len >> 8) & 0xff); 137 try { 138 mOut.write(buf, 0, 2); 139 mOut.write(cmd, 0, len); 140 } catch (IOException ex) { 141 Slog.e(TAG, "write error"); 142 disconnect(); 143 return false; 144 } 145 return true; 146 } 147 148 private synchronized String transaction(String cmd) { 149 if (!connect()) { 150 Slog.e(TAG, "connection failed"); 151 return "-1"; 152 } 153 154 if (!writeCommand(cmd)) { 155 /* 156 * If installd died and restarted in the background (unlikely but 157 * possible) we'll fail on the next write (this one). Try to 158 * reconnect and write the command one more time before giving up. 159 */ 160 Slog.e(TAG, "write command failed? reconnect!"); 161 if (!connect() || !writeCommand(cmd)) { 162 return "-1"; 163 } 164 } 165 if (LOCAL_DEBUG) { 166 Slog.i(TAG, "send: '" + cmd + "'"); 167 } 168 if (readReply()) { 169 String s = new String(buf, 0, buflen); 170 if (LOCAL_DEBUG) { 171 Slog.i(TAG, "recv: '" + s + "'"); 172 } 173 return s; 174 } else { 175 if (LOCAL_DEBUG) { 176 Slog.i(TAG, "fail"); 177 } 178 return "-1"; 179 } 180 } 181 182 private int execute(String cmd) { 183 String res = transaction(cmd); 184 try { 185 return Integer.parseInt(res); 186 } catch (NumberFormatException ex) { 187 return -1; 188 } 189 } 190 191 public int install(String name, int uid, int gid) { 192 StringBuilder builder = new StringBuilder("install"); 193 builder.append(' '); 194 builder.append(name); 195 builder.append(' '); 196 builder.append(uid); 197 builder.append(' '); 198 builder.append(gid); 199 return execute(builder.toString()); 200 } 201 202 public int dexopt(String apkPath, int uid, boolean isPublic) { 203 StringBuilder builder = new StringBuilder("dexopt"); 204 builder.append(' '); 205 builder.append(apkPath); 206 builder.append(' '); 207 builder.append(uid); 208 builder.append(isPublic ? " 1" : " 0"); 209 return execute(builder.toString()); 210 } 211 212 public int movedex(String srcPath, String dstPath) { 213 StringBuilder builder = new StringBuilder("movedex"); 214 builder.append(' '); 215 builder.append(srcPath); 216 builder.append(' '); 217 builder.append(dstPath); 218 return execute(builder.toString()); 219 } 220 221 public int rmdex(String codePath) { 222 StringBuilder builder = new StringBuilder("rmdex"); 223 builder.append(' '); 224 builder.append(codePath); 225 return execute(builder.toString()); 226 } 227 228 public int remove(String name, int userId) { 229 StringBuilder builder = new StringBuilder("remove"); 230 builder.append(' '); 231 builder.append(name); 232 builder.append(' '); 233 builder.append(userId); 234 return execute(builder.toString()); 235 } 236 237 public int rename(String oldname, String newname) { 238 StringBuilder builder = new StringBuilder("rename"); 239 builder.append(' '); 240 builder.append(oldname); 241 builder.append(' '); 242 builder.append(newname); 243 return execute(builder.toString()); 244 } 245 246 public int fixUid(String name, int uid, int gid) { 247 StringBuilder builder = new StringBuilder("fixuid"); 248 builder.append(' '); 249 builder.append(name); 250 builder.append(' '); 251 builder.append(uid); 252 builder.append(' '); 253 builder.append(gid); 254 return execute(builder.toString()); 255 } 256 257 public int deleteCacheFiles(String name) { 258 StringBuilder builder = new StringBuilder("rmcache"); 259 builder.append(' '); 260 builder.append(name); 261 return execute(builder.toString()); 262 } 263 264 public int createUserData(String name, int uid, int userId) { 265 StringBuilder builder = new StringBuilder("mkuserdata"); 266 builder.append(' '); 267 builder.append(name); 268 builder.append(' '); 269 builder.append(uid); 270 builder.append(' '); 271 builder.append(userId); 272 return execute(builder.toString()); 273 } 274 275 public int removeUserDataDirs(int userId) { 276 StringBuilder builder = new StringBuilder("rmuser"); 277 builder.append(' '); 278 builder.append(userId); 279 return execute(builder.toString()); 280 } 281 282 public int clearUserData(String name, int userId) { 283 StringBuilder builder = new StringBuilder("rmuserdata"); 284 builder.append(' '); 285 builder.append(name); 286 builder.append(' '); 287 builder.append(userId); 288 return execute(builder.toString()); 289 } 290 291 /** 292 * Clone all the package data directories from srcUserId to targetUserId. If copyData is true, 293 * some of the data is also copied, otherwise just empty directories are created with the 294 * correct access rights. 295 * @param srcUserId user to copy the data directories from 296 * @param targetUserId user to copy the data directories to 297 * @param copyData whether the data itself is to be copied. If false, empty directories are 298 * created. 299 * @return success/error code 300 */ 301 public int cloneUserData(int srcUserId, int targetUserId, boolean copyData) { 302 StringBuilder builder = new StringBuilder("cloneuserdata"); 303 builder.append(' '); 304 builder.append(srcUserId); 305 builder.append(' '); 306 builder.append(targetUserId); 307 builder.append(' '); 308 builder.append(copyData ? '1' : '0'); 309 return execute(builder.toString()); 310 } 311 312 public boolean ping() { 313 if (execute("ping") < 0) { 314 return false; 315 } else { 316 return true; 317 } 318 } 319 320 public int freeCache(long freeStorageSize) { 321 StringBuilder builder = new StringBuilder("freecache"); 322 builder.append(' '); 323 builder.append(String.valueOf(freeStorageSize)); 324 return execute(builder.toString()); 325 } 326 327 /* 328 * @param packagePathSuffix The name of the path relative to install 329 * directory. Say if the path name is /data/app/com.test-1.apk, the package 330 * suffix path will be com.test-1 331 */ 332 public int setForwardLockPerm(String packagePathSuffix, int gid) { 333 StringBuilder builder = new StringBuilder("protect"); 334 builder.append(' '); 335 builder.append(packagePathSuffix); 336 builder.append(' '); 337 builder.append(gid); 338 return execute(builder.toString()); 339 } 340 341 public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, 342 String asecPath, PackageStats pStats) { 343 StringBuilder builder = new StringBuilder("getsize"); 344 builder.append(' '); 345 builder.append(pkgName); 346 builder.append(' '); 347 builder.append(apkPath); 348 builder.append(' '); 349 builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); 350 builder.append(' '); 351 builder.append(asecPath != null ? asecPath : "!"); 352 353 String s = transaction(builder.toString()); 354 String res[] = s.split(" "); 355 356 if ((res == null) || (res.length != 5)) { 357 return -1; 358 } 359 try { 360 pStats.codeSize = Long.parseLong(res[1]); 361 pStats.dataSize = Long.parseLong(res[2]); 362 pStats.cacheSize = Long.parseLong(res[3]); 363 pStats.externalCodeSize = Long.parseLong(res[4]); 364 return Integer.parseInt(res[0]); 365 } catch (NumberFormatException e) { 366 return -1; 367 } 368 } 369 370 public int moveFiles() { 371 return execute("movefiles"); 372 } 373 374 public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath) { 375 if (dataPath == null) { 376 Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null"); 377 return -1; 378 } else if (nativeLibPath == null) { 379 Slog.e(TAG, "unlinkNativeLibraryDirectory nativeLibPath is null"); 380 return -1; 381 } 382 383 StringBuilder builder = new StringBuilder("linklib "); 384 builder.append(dataPath); 385 builder.append(' '); 386 builder.append(nativeLibPath); 387 388 return execute(builder.toString()); 389 } 390 391 public int unlinkNativeLibraryDirectory(String dataPath) { 392 if (dataPath == null) { 393 Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null"); 394 return -1; 395 } 396 397 StringBuilder builder = new StringBuilder("unlinklib "); 398 builder.append(dataPath); 399 400 return execute(builder.toString()); 401 } 402 } 403