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