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.internal.os; 18 19import android.net.LocalSocket; 20import android.net.LocalSocketAddress; 21import android.util.Slog; 22import libcore.io.IoUtils; 23import libcore.io.Streams; 24 25import java.io.IOException; 26import java.io.InputStream; 27import java.io.OutputStream; 28 29/** 30 * Represents a connection to {@code installd}. Allows multiple connect and 31 * disconnect cycles. 32 * 33 * @hide for internal use only 34 */ 35public class InstallerConnection { 36 private static final String TAG = "InstallerConnection"; 37 private static final boolean LOCAL_DEBUG = false; 38 39 private InputStream mIn; 40 private OutputStream mOut; 41 private LocalSocket mSocket; 42 43 private final byte buf[] = new byte[1024]; 44 45 public InstallerConnection() { 46 } 47 48 public synchronized String transact(String cmd) { 49 if (!connect()) { 50 Slog.e(TAG, "connection failed"); 51 return "-1"; 52 } 53 54 if (!writeCommand(cmd)) { 55 /* 56 * If installd died and restarted in the background (unlikely but 57 * possible) we'll fail on the next write (this one). Try to 58 * reconnect and write the command one more time before giving up. 59 */ 60 Slog.e(TAG, "write command failed? reconnect!"); 61 if (!connect() || !writeCommand(cmd)) { 62 return "-1"; 63 } 64 } 65 if (LOCAL_DEBUG) { 66 Slog.i(TAG, "send: '" + cmd + "'"); 67 } 68 69 final int replyLength = readReply(); 70 if (replyLength > 0) { 71 String s = new String(buf, 0, replyLength); 72 if (LOCAL_DEBUG) { 73 Slog.i(TAG, "recv: '" + s + "'"); 74 } 75 return s; 76 } else { 77 if (LOCAL_DEBUG) { 78 Slog.i(TAG, "fail"); 79 } 80 return "-1"; 81 } 82 } 83 84 public int execute(String cmd) { 85 String res = transact(cmd); 86 try { 87 return Integer.parseInt(res); 88 } catch (NumberFormatException ex) { 89 return -1; 90 } 91 } 92 93 public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) { 94 return dexopt(apkPath, uid, isPublic, "*", instructionSet, false); 95 } 96 97 public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName, 98 String instructionSet, boolean vmSafeMode) { 99 StringBuilder builder = new StringBuilder("dexopt"); 100 builder.append(' '); 101 builder.append(apkPath); 102 builder.append(' '); 103 builder.append(uid); 104 builder.append(isPublic ? " 1" : " 0"); 105 builder.append(' '); 106 builder.append(pkgName); 107 builder.append(' '); 108 builder.append(instructionSet); 109 builder.append(' '); 110 builder.append(vmSafeMode ? " 1" : " 0"); 111 return execute(builder.toString()); 112 } 113 114 public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) { 115 return patchoat(apkPath, uid, isPublic, "*", instructionSet); 116 } 117 118 public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName, 119 String instructionSet) { 120 StringBuilder builder = new StringBuilder("patchoat"); 121 builder.append(' '); 122 builder.append(apkPath); 123 builder.append(' '); 124 builder.append(uid); 125 builder.append(isPublic ? " 1" : " 0"); 126 builder.append(' '); 127 builder.append(pkgName); 128 builder.append(' '); 129 builder.append(instructionSet); 130 return execute(builder.toString()); 131 } 132 133 private boolean connect() { 134 if (mSocket != null) { 135 return true; 136 } 137 Slog.i(TAG, "connecting..."); 138 try { 139 mSocket = new LocalSocket(); 140 141 LocalSocketAddress address = new LocalSocketAddress("installd", 142 LocalSocketAddress.Namespace.RESERVED); 143 144 mSocket.connect(address); 145 146 mIn = mSocket.getInputStream(); 147 mOut = mSocket.getOutputStream(); 148 } catch (IOException ex) { 149 disconnect(); 150 return false; 151 } 152 return true; 153 } 154 155 public void disconnect() { 156 Slog.i(TAG, "disconnecting..."); 157 IoUtils.closeQuietly(mSocket); 158 IoUtils.closeQuietly(mIn); 159 IoUtils.closeQuietly(mOut); 160 161 mSocket = null; 162 mIn = null; 163 mOut = null; 164 } 165 166 167 private boolean readFully(byte[] buffer, int len) { 168 try { 169 Streams.readFully(mIn, buffer, 0, len); 170 } catch (IOException ioe) { 171 Slog.e(TAG, "read exception"); 172 disconnect(); 173 return false; 174 } 175 176 if (LOCAL_DEBUG) { 177 Slog.i(TAG, "read " + len + " bytes"); 178 } 179 180 return true; 181 } 182 183 private int readReply() { 184 if (!readFully(buf, 2)) { 185 return -1; 186 } 187 188 final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); 189 if ((len < 1) || (len > buf.length)) { 190 Slog.e(TAG, "invalid reply length (" + len + ")"); 191 disconnect(); 192 return -1; 193 } 194 195 if (!readFully(buf, len)) { 196 return -1; 197 } 198 199 return len; 200 } 201 202 private boolean writeCommand(String cmdString) { 203 final byte[] cmd = cmdString.getBytes(); 204 final int len = cmd.length; 205 if ((len < 1) || (len > buf.length)) { 206 return false; 207 } 208 209 buf[0] = (byte) (len & 0xff); 210 buf[1] = (byte) ((len >> 8) & 0xff); 211 try { 212 mOut.write(buf, 0, 2); 213 mOut.write(cmd, 0, len); 214 } catch (IOException ex) { 215 Slog.e(TAG, "write error"); 216 disconnect(); 217 return false; 218 } 219 return true; 220 } 221} 222