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; 18 19import android.content.pm.PackageStats; 20import android.net.LocalSocketAddress; 21import android.net.LocalSocket; 22import android.util.Config; 23import android.util.Slog; 24 25import java.io.IOException; 26import java.io.InputStream; 27import java.io.OutputStream; 28import java.net.Socket; 29 30 31class 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, int uid, int gid) { 170 StringBuilder builder = new StringBuilder("install"); 171 builder.append(' '); 172 builder.append(name); 173 builder.append(' '); 174 builder.append(uid); 175 builder.append(' '); 176 builder.append(gid); 177 return execute(builder.toString()); 178 } 179 180 public int dexopt(String apkPath, int uid, boolean isPublic) { 181 StringBuilder builder = new StringBuilder("dexopt"); 182 builder.append(' '); 183 builder.append(apkPath); 184 builder.append(' '); 185 builder.append(uid); 186 builder.append(isPublic ? " 1" : " 0"); 187 return execute(builder.toString()); 188 } 189 190 public int movedex(String srcPath, String dstPath) { 191 StringBuilder builder = new StringBuilder("movedex"); 192 builder.append(' '); 193 builder.append(srcPath); 194 builder.append(' '); 195 builder.append(dstPath); 196 return execute(builder.toString()); 197 } 198 199 public int rmdex(String codePath) { 200 StringBuilder builder = new StringBuilder("rmdex"); 201 builder.append(' '); 202 builder.append(codePath); 203 return execute(builder.toString()); 204 } 205 206 public int remove(String name) { 207 StringBuilder builder = new StringBuilder("remove"); 208 builder.append(' '); 209 builder.append(name); 210 return execute(builder.toString()); 211 } 212 213 public int rename(String oldname, String newname) { 214 StringBuilder builder = new StringBuilder("rename"); 215 builder.append(' '); 216 builder.append(oldname); 217 builder.append(' '); 218 builder.append(newname); 219 return execute(builder.toString()); 220 } 221 222 public int deleteCacheFiles(String name) { 223 StringBuilder builder = new StringBuilder("rmcache"); 224 builder.append(' '); 225 builder.append(name); 226 return execute(builder.toString()); 227 } 228 229 public int clearUserData(String name) { 230 StringBuilder builder = new StringBuilder("rmuserdata"); 231 builder.append(' '); 232 builder.append(name); 233 return execute(builder.toString()); 234 } 235 236 public boolean ping() { 237 if (execute("ping") < 0) { 238 return false; 239 } else { 240 return true; 241 } 242 } 243 244 public int freeCache(long freeStorageSize) { 245 StringBuilder builder = new StringBuilder("freecache"); 246 builder.append(' '); 247 builder.append(String.valueOf(freeStorageSize)); 248 return execute(builder.toString()); 249 } 250 251 /* 252 * @param packagePathSuffix The name of the path relative to install 253 * directory. Say if the path name is /data/app/com.test-1.apk, 254 * the package suffix path will be com.test-1 255 */ 256 public int setForwardLockPerm(String packagePathSuffix, int gid) { 257 StringBuilder builder = new StringBuilder("protect"); 258 builder.append(' '); 259 builder.append(packagePathSuffix); 260 builder.append(' '); 261 builder.append(gid); 262 return execute(builder.toString()); 263 } 264 265 public int getSizeInfo(String pkgName, String apkPath, 266 String fwdLockApkPath, PackageStats pStats) { 267 StringBuilder builder = new StringBuilder("getsize"); 268 builder.append(' '); 269 builder.append(pkgName); 270 builder.append(' '); 271 builder.append(apkPath); 272 builder.append(' '); 273 builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); 274 String s = transaction(builder.toString()); 275 String res[] = s.split(" "); 276 277 if((res == null) || (res.length != 4)) { 278 return -1; 279 } 280 try { 281 pStats.codeSize = Long.parseLong(res[1]); 282 pStats.dataSize = Long.parseLong(res[2]); 283 pStats.cacheSize = Long.parseLong(res[3]); 284 return Integer.parseInt(res[0]); 285 } catch (NumberFormatException e) { 286 return -1; 287 } 288 } 289 290 public int moveFiles() { 291 return execute("movefiles"); 292 } 293} 294