• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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