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