blob: 47f2c708c1a0d801286dbc2c8b218e4c501d9709 [file] [log] [blame]
Narayan Kamath29564cd2014-08-07 10:57:40 +01001/*
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;
Makoto Onukic8a2cfe2015-06-23 16:33:48 -070021import android.os.SystemClock;
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -070022import android.text.TextUtils;
Narayan Kamath29564cd2014-08-07 10:57:40 +010023import android.util.Slog;
Todd Kennedyfa54ab72015-09-25 07:46:12 -070024
Jeff Sharkey8948c012015-11-03 12:33:54 -080025import com.android.internal.util.Preconditions;
26
Narayan Kamath29564cd2014-08-07 10:57:40 +010027import libcore.io.IoUtils;
28import libcore.io.Streams;
29
30import java.io.IOException;
31import java.io.InputStream;
32import java.io.OutputStream;
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -070033import java.util.Arrays;
Narayan Kamath29564cd2014-08-07 10:57:40 +010034
35/**
36 * Represents a connection to {@code installd}. Allows multiple connect and
37 * disconnect cycles.
38 *
39 * @hide for internal use only
40 */
41public 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
Jeff Sharkey8948c012015-11-03 12:33:54 -080049 private volatile Object mWarnIfHeld;
50
Narayan Kamath29564cd2014-08-07 10:57:40 +010051 private final byte buf[] = new byte[1024];
52
53 public InstallerConnection() {
54 }
55
Jeff Sharkey8948c012015-11-03 12:33:54 -080056 /**
57 * Yell loudly if someone tries making future calls while holding a lock on
58 * the given object.
59 */
60 public void setWarnIfHeld(Object warnIfHeld) {
61 Preconditions.checkState(mWarnIfHeld == null);
62 mWarnIfHeld = Preconditions.checkNotNull(warnIfHeld);
63 }
64
Narayan Kamath29564cd2014-08-07 10:57:40 +010065 public synchronized String transact(String cmd) {
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -070066 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
Narayan Kamath29564cd2014-08-07 10:57:40 +010071 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
Jeff Sharkey42884192016-04-09 16:12:01 -0600106 public String[] execute(String cmd, Object... args) throws InstallerException {
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -0700107 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 }
Jeff Sharkey42884192016-04-09 16:12:01 -0600124 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;
Calin Juravlefeb19302014-08-21 19:00:15 +0100135 }
136
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -0700137 public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
Andreas Gampebdd30d82016-03-20 11:32:11 -0700138 int dexFlags, String compilerFilter, String volumeUuid) throws InstallerException {
Calin Juravledb4a79a2015-12-23 18:55:08 +0200139 dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
Andreas Gampebdd30d82016-03-20 11:32:11 -0700140 null /*outputPath*/, dexFlags, compilerFilter, volumeUuid);
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -0700141 }
142
143 public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
Andreas Gampebdd30d82016-03-20 11:32:11 -0700144 int dexoptNeeded, String outputPath, int dexFlags, String compilerFilter,
145 String volumeUuid) throws InstallerException {
Calin Juravledb4a79a2015-12-23 18:55:08 +0200146 execute("dexopt",
147 apkPath,
148 uid,
149 pkgName,
150 instructionSet,
151 dexoptNeeded,
152 outputPath,
153 dexFlags,
Andreas Gampebdd30d82016-03-20 11:32:11 -0700154 compilerFilter,
155 volumeUuid);
156 }
157
158 public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
Jeff Sharkey42884192016-04-09 16:12:01 -0600159 final String[] res = execute("merge_profiles", uid, pkgName);
Andreas Gampebdd30d82016-03-20 11:32:11 -0700160
161 if ((res == null) || (res.length != 2)) {
Jeff Sharkey42884192016-04-09 16:12:01 -0600162 throw new InstallerException("Invalid size result: " + Arrays.toString(res));
Andreas Gampebdd30d82016-03-20 11:32:11 -0700163 }
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")) {
Jeff Sharkey42884192016-04-09 16:12:01 -0600167 throw new InstallerException("Invalid boolean result: " + Arrays.toString(res));
Andreas Gampebdd30d82016-03-20 11:32:11 -0700168 }
169
170 return Boolean.parseBoolean(res[1]);
Narayan Kamath29564cd2014-08-07 10:57:40 +0100171 }
172
Narayan Kamath29564cd2014-08-07 10:57:40 +0100173 private boolean connect() {
174 if (mSocket != null) {
175 return true;
176 }
177 Slog.i(TAG, "connecting...");
178 try {
179 mSocket = new LocalSocket();
180
181 LocalSocketAddress address = new LocalSocketAddress("installd",
182 LocalSocketAddress.Namespace.RESERVED);
183
184 mSocket.connect(address);
185
186 mIn = mSocket.getInputStream();
187 mOut = mSocket.getOutputStream();
188 } catch (IOException ex) {
189 disconnect();
190 return false;
191 }
192 return true;
193 }
194
195 public void disconnect() {
196 Slog.i(TAG, "disconnecting...");
197 IoUtils.closeQuietly(mSocket);
198 IoUtils.closeQuietly(mIn);
199 IoUtils.closeQuietly(mOut);
200
201 mSocket = null;
202 mIn = null;
203 mOut = null;
204 }
205
206
207 private boolean readFully(byte[] buffer, int len) {
208 try {
209 Streams.readFully(mIn, buffer, 0, len);
210 } catch (IOException ioe) {
211 Slog.e(TAG, "read exception");
212 disconnect();
213 return false;
214 }
215
216 if (LOCAL_DEBUG) {
217 Slog.i(TAG, "read " + len + " bytes");
218 }
219
220 return true;
221 }
222
223 private int readReply() {
224 if (!readFully(buf, 2)) {
225 return -1;
226 }
227
228 final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
229 if ((len < 1) || (len > buf.length)) {
230 Slog.e(TAG, "invalid reply length (" + len + ")");
231 disconnect();
232 return -1;
233 }
234
235 if (!readFully(buf, len)) {
236 return -1;
237 }
238
239 return len;
240 }
241
242 private boolean writeCommand(String cmdString) {
243 final byte[] cmd = cmdString.getBytes();
244 final int len = cmd.length;
245 if ((len < 1) || (len > buf.length)) {
246 return false;
247 }
248
249 buf[0] = (byte) (len & 0xff);
250 buf[1] = (byte) ((len >> 8) & 0xff);
251 try {
252 mOut.write(buf, 0, 2);
253 mOut.write(cmd, 0, len);
254 } catch (IOException ex) {
255 Slog.e(TAG, "write error");
256 disconnect();
257 return false;
258 }
259 return true;
260 }
Makoto Onukic8a2cfe2015-06-23 16:33:48 -0700261
262 public void waitForConnection() {
263 for (;;) {
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -0700264 try {
265 execute("ping");
Makoto Onukic8a2cfe2015-06-23 16:33:48 -0700266 return;
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -0700267 } catch (InstallerException ignored) {
Makoto Onukic8a2cfe2015-06-23 16:33:48 -0700268 }
269 Slog.w(TAG, "installd not ready");
270 SystemClock.sleep(1000);
271 }
272 }
Jeff Sharkeyfdeeeea2016-01-11 17:34:24 -0700273
274 public static class InstallerException extends Exception {
275 public InstallerException(String detailMessage) {
276 super(detailMessage);
277 }
278 }
Narayan Kamath29564cd2014-08-07 10:57:40 +0100279}