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.internal.os; 18 19 import android.net.LocalSocket; 20 import android.net.LocalSocketAddress; 21 import android.os.SystemClock; 22 import android.text.TextUtils; 23 import android.util.Slog; 24 25 import com.android.internal.util.Preconditions; 26 27 import libcore.io.IoUtils; 28 import libcore.io.Streams; 29 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.OutputStream; 33 import java.util.Arrays; 34 35 /** 36 * Represents a connection to {@code installd}. Allows multiple connect and 37 * disconnect cycles. 38 * 39 * @hide for internal use only 40 */ 41 public class InstallerConnection { 42 private static final String TAG = "InstallerConnection"; 43 private static final boolean LOCAL_DEBUG = false; 44 45 private InputStream mIn; 46 private OutputStream mOut; 47 private LocalSocket mSocket; 48 49 private volatile Object mWarnIfHeld; 50 51 private final byte buf[] = new byte[1024]; 52 InstallerConnection()53 public InstallerConnection() { 54 } 55 56 /** 57 * Yell loudly if someone tries making future calls while holding a lock on 58 * the given object. 59 */ setWarnIfHeld(Object warnIfHeld)60 public void setWarnIfHeld(Object warnIfHeld) { 61 Preconditions.checkState(mWarnIfHeld == null); 62 mWarnIfHeld = Preconditions.checkNotNull(warnIfHeld); 63 } 64 transact(String cmd)65 public synchronized String transact(String cmd) { 66 if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) { 67 Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x" 68 + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable()); 69 } 70 71 if (!connect()) { 72 Slog.e(TAG, "connection failed"); 73 return "-1"; 74 } 75 76 if (!writeCommand(cmd)) { 77 /* 78 * If installd died and restarted in the background (unlikely but 79 * possible) we'll fail on the next write (this one). Try to 80 * reconnect and write the command one more time before giving up. 81 */ 82 Slog.e(TAG, "write command failed? reconnect!"); 83 if (!connect() || !writeCommand(cmd)) { 84 return "-1"; 85 } 86 } 87 if (LOCAL_DEBUG) { 88 Slog.i(TAG, "send: '" + cmd + "'"); 89 } 90 91 final int replyLength = readReply(); 92 if (replyLength > 0) { 93 String s = new String(buf, 0, replyLength); 94 if (LOCAL_DEBUG) { 95 Slog.i(TAG, "recv: '" + s + "'"); 96 } 97 return s; 98 } else { 99 if (LOCAL_DEBUG) { 100 Slog.i(TAG, "fail"); 101 } 102 return "-1"; 103 } 104 } 105 execute(String cmd, Object... args)106 public String[] execute(String cmd, Object... args) throws InstallerException { 107 final StringBuilder builder = new StringBuilder(cmd); 108 for (Object arg : args) { 109 String escaped; 110 if (arg == null) { 111 escaped = ""; 112 } else { 113 escaped = String.valueOf(arg); 114 } 115 if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) { 116 throw new InstallerException( 117 "Invalid argument while executing " + cmd + " " + Arrays.toString(args)); 118 } 119 if (TextUtils.isEmpty(escaped)) { 120 escaped = "!"; 121 } 122 builder.append(' ').append(escaped); 123 } 124 final String[] resRaw = transact(builder.toString()).split(" "); 125 int res = -1; 126 try { 127 res = Integer.parseInt(resRaw[0]); 128 } catch (ArrayIndexOutOfBoundsException | NumberFormatException ignored) { 129 } 130 if (res != 0) { 131 throw new InstallerException( 132 "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res); 133 } 134 return resRaw; 135 } 136 dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded, int dexFlags, String compilerFilter, String volumeUuid, String sharedLibraries)137 public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded, 138 int dexFlags, String compilerFilter, String volumeUuid, String sharedLibraries) 139 throws InstallerException { 140 dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, null /*outputPath*/, dexFlags, 141 compilerFilter, volumeUuid, sharedLibraries); 142 } 143 dexopt(String apkPath, int uid, String pkgName, String instructionSet, int dexoptNeeded, String outputPath, int dexFlags, String compilerFilter, String volumeUuid, String sharedLibraries)144 public void dexopt(String apkPath, int uid, String pkgName, String instructionSet, 145 int dexoptNeeded, String outputPath, int dexFlags, String compilerFilter, 146 String volumeUuid, String sharedLibraries) throws InstallerException { 147 execute("dexopt", 148 apkPath, 149 uid, 150 pkgName, 151 instructionSet, 152 dexoptNeeded, 153 outputPath, 154 dexFlags, 155 compilerFilter, 156 volumeUuid, 157 sharedLibraries); 158 } 159 safeParseBooleanResult(String[] res)160 private boolean safeParseBooleanResult(String[] res) throws InstallerException { 161 if ((res == null) || (res.length != 2)) { 162 throw new InstallerException("Invalid size result: " + Arrays.toString(res)); 163 } 164 165 // Just as a sanity check. Anything != "true" will be interpreted as false by parseBoolean. 166 if (!res[1].equals("true") && !res[1].equals("false")) { 167 throw new InstallerException("Invalid boolean result: " + Arrays.toString(res)); 168 } 169 170 return Boolean.parseBoolean(res[1]); 171 } 172 mergeProfiles(int uid, String pkgName)173 public boolean mergeProfiles(int uid, String pkgName) throws InstallerException { 174 final String[] res = execute("merge_profiles", uid, pkgName); 175 176 return safeParseBooleanResult(res); 177 } 178 dumpProfiles(String gid, String packageName, String codePaths)179 public boolean dumpProfiles(String gid, String packageName, String codePaths) 180 throws InstallerException { 181 final String[] res = execute("dump_profiles", gid, packageName, codePaths); 182 183 return safeParseBooleanResult(res); 184 } 185 connect()186 private boolean connect() { 187 if (mSocket != null) { 188 return true; 189 } 190 Slog.i(TAG, "connecting..."); 191 try { 192 mSocket = new LocalSocket(); 193 194 LocalSocketAddress address = new LocalSocketAddress("installd", 195 LocalSocketAddress.Namespace.RESERVED); 196 197 mSocket.connect(address); 198 199 mIn = mSocket.getInputStream(); 200 mOut = mSocket.getOutputStream(); 201 } catch (IOException ex) { 202 disconnect(); 203 return false; 204 } 205 return true; 206 } 207 disconnect()208 public void disconnect() { 209 Slog.i(TAG, "disconnecting..."); 210 IoUtils.closeQuietly(mSocket); 211 IoUtils.closeQuietly(mIn); 212 IoUtils.closeQuietly(mOut); 213 214 mSocket = null; 215 mIn = null; 216 mOut = null; 217 } 218 219 readFully(byte[] buffer, int len)220 private boolean readFully(byte[] buffer, int len) { 221 try { 222 Streams.readFully(mIn, buffer, 0, len); 223 } catch (IOException ioe) { 224 Slog.e(TAG, "read exception"); 225 disconnect(); 226 return false; 227 } 228 229 if (LOCAL_DEBUG) { 230 Slog.i(TAG, "read " + len + " bytes"); 231 } 232 233 return true; 234 } 235 readReply()236 private int readReply() { 237 if (!readFully(buf, 2)) { 238 return -1; 239 } 240 241 final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); 242 if ((len < 1) || (len > buf.length)) { 243 Slog.e(TAG, "invalid reply length (" + len + ")"); 244 disconnect(); 245 return -1; 246 } 247 248 if (!readFully(buf, len)) { 249 return -1; 250 } 251 252 return len; 253 } 254 writeCommand(String cmdString)255 private boolean writeCommand(String cmdString) { 256 final byte[] cmd = cmdString.getBytes(); 257 final int len = cmd.length; 258 if ((len < 1) || (len > buf.length)) { 259 return false; 260 } 261 262 buf[0] = (byte) (len & 0xff); 263 buf[1] = (byte) ((len >> 8) & 0xff); 264 try { 265 mOut.write(buf, 0, 2); 266 mOut.write(cmd, 0, len); 267 } catch (IOException ex) { 268 Slog.e(TAG, "write error"); 269 disconnect(); 270 return false; 271 } 272 return true; 273 } 274 waitForConnection()275 public void waitForConnection() { 276 for (;;) { 277 try { 278 execute("ping"); 279 return; 280 } catch (InstallerException ignored) { 281 } 282 Slog.w(TAG, "installd not ready"); 283 SystemClock.sleep(1000); 284 } 285 } 286 287 public static class InstallerException extends Exception { InstallerException(String detailMessage)288 public InstallerException(String detailMessage) { 289 super(detailMessage); 290 } 291 } 292 } 293