blob: 39c847ab226d48d015c7e2346316a5e94901d9dc [file] [log] [blame]
San Mehat67bd2cd2010-01-12 12:18:49 -08001/*
2 * Copyright (C) 2007 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.server;
18
19import android.net.LocalSocketAddress;
20import android.net.LocalSocket;
21import android.os.Environment;
22import android.os.SystemClock;
23import android.os.SystemProperties;
Joe Onorato8a9b2202010-02-26 18:56:32 -080024import android.util.Slog;
San Mehat67bd2cd2010-01-12 12:18:49 -080025
26import java.io.IOException;
27import java.io.InputStream;
28import java.io.OutputStream;
29import java.net.Socket;
San Mehat67bd2cd2010-01-12 12:18:49 -080030
31import java.util.List;
32import java.util.ArrayList;
33import java.util.ListIterator;
34import java.util.concurrent.BlockingQueue;
35import java.util.concurrent.LinkedBlockingQueue;
36
37/**
38 * Generic connector class for interfacing with a native
39 * daemon which uses the libsysutils FrameworkListener
40 * protocol.
41 */
42final class NativeDaemonConnector implements Runnable {
San Mehat1ff43712010-02-04 15:09:02 -080043 private static final boolean LOCAL_LOGD = false;
San Mehat67bd2cd2010-01-12 12:18:49 -080044
45 private BlockingQueue<String> mResponseQueue;
46 private OutputStream mOutputStream;
47 private String TAG = "NativeDaemonConnector";
48 private String mSocket;
49 private INativeDaemonConnectorCallbacks mCallbacks;
50
51 class ResponseCode {
52 public static final int ActionInitiated = 100;
53
54 public static final int CommandOkay = 200;
55
56 // The range of 400 -> 599 is reserved for cmd failures
57 public static final int OperationFailed = 400;
58 public static final int CommandSyntaxError = 500;
59 public static final int CommandParameterError = 501;
60
61 public static final int UnsolicitedInformational = 600;
62
63 //
64 public static final int FailedRangeStart = 400;
65 public static final int FailedRangeEnd = 599;
66 }
67
68 NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks,
69 String socket, int responseQueueSize, String logTag) {
70 mCallbacks = callbacks;
71 if (logTag != null)
72 TAG = logTag;
73 mSocket = socket;
74 mResponseQueue = new LinkedBlockingQueue<String>(responseQueueSize);
75 }
76
77 public void run() {
78
79 while (true) {
80 try {
81 listenToSocket();
82 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -080083 Slog.e(TAG, "Error in NativeDaemonConnector", e);
San Mehat4c27e0e2010-01-29 05:22:17 -080084 SystemClock.sleep(5000);
San Mehat67bd2cd2010-01-12 12:18:49 -080085 }
86 }
87 }
88
San Mehat4c27e0e2010-01-29 05:22:17 -080089 private void listenToSocket() throws IOException {
San Mehat67bd2cd2010-01-12 12:18:49 -080090 LocalSocket socket = null;
91
92 try {
93 socket = new LocalSocket();
94 LocalSocketAddress address = new LocalSocketAddress(mSocket,
95 LocalSocketAddress.Namespace.RESERVED);
96
97 socket.connect(address);
98 mCallbacks.onDaemonConnected();
99
100 InputStream inputStream = socket.getInputStream();
101 mOutputStream = socket.getOutputStream();
102
103 byte[] buffer = new byte[4096];
104
105 while (true) {
106 int count = inputStream.read(buffer);
107 if (count < 0) break;
108
109 int start = 0;
110 for (int i = 0; i < count; i++) {
111 if (buffer[i] == 0) {
112 String event = new String(buffer, start, i - start);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800113 if (LOCAL_LOGD) Slog.d(TAG, String.format("RCV <- {%s}", event));
San Mehat67bd2cd2010-01-12 12:18:49 -0800114
115 String[] tokens = event.split(" ");
116 try {
117 int code = Integer.parseInt(tokens[0]);
118
119 if (code >= ResponseCode.UnsolicitedInformational) {
120 try {
121 if (!mCallbacks.onEvent(code, event, tokens)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800122 Slog.w(TAG, String.format(
San Mehat67bd2cd2010-01-12 12:18:49 -0800123 "Unhandled event (%s)", event));
124 }
125 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800126 Slog.e(TAG, String.format(
San Mehat67bd2cd2010-01-12 12:18:49 -0800127 "Error handling '%s'", event), ex);
128 }
129 } else {
130 try {
131 mResponseQueue.put(event);
132 } catch (InterruptedException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800133 Slog.e(TAG, "Failed to put response onto queue", ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800134 }
135 }
136 } catch (NumberFormatException nfe) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800137 Slog.w(TAG, String.format("Bad msg (%s)", event));
San Mehat67bd2cd2010-01-12 12:18:49 -0800138 }
139 start = i + 1;
140 }
141 }
142 }
143 } catch (IOException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800144 Slog.e(TAG, "Communications error", ex);
San Mehat4c27e0e2010-01-29 05:22:17 -0800145 throw ex;
146 } finally {
147 synchronized (this) {
148 if (mOutputStream != null) {
149 try {
150 mOutputStream.close();
151 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800152 Slog.w(TAG, "Failed closing output stream", e);
San Mehat4c27e0e2010-01-29 05:22:17 -0800153 }
154 mOutputStream = null;
San Mehat67bd2cd2010-01-12 12:18:49 -0800155 }
San Mehat4c27e0e2010-01-29 05:22:17 -0800156 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800157
San Mehat4c27e0e2010-01-29 05:22:17 -0800158 try {
159 if (socket != null) {
160 socket.close();
161 }
162 } catch (IOException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800163 Slog.w(TAG, "Failed closing socket", ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800164 }
165 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800166 }
167
168 private void sendCommand(String command) {
169 sendCommand(command, null);
170 }
171
172 /**
173 * Sends a command to the daemon with a single argument
174 *
175 * @param command The command to send to the daemon
176 * @param argument The argument to send with the command (or null)
177 */
178 private void sendCommand(String command, String argument) {
179 synchronized (this) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800180 if (LOCAL_LOGD) Slog.d(TAG, String.format("SND -> {%s} {%s}", command, argument));
San Mehat67bd2cd2010-01-12 12:18:49 -0800181 if (mOutputStream == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800182 Slog.e(TAG, "No connection to daemon", new IllegalStateException());
San Mehat67bd2cd2010-01-12 12:18:49 -0800183 } else {
184 StringBuilder builder = new StringBuilder(command);
185 if (argument != null) {
186 builder.append(argument);
187 }
188 builder.append('\0');
189
190 try {
191 mOutputStream.write(builder.toString().getBytes());
192 } catch (IOException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800193 Slog.e(TAG, "IOException in sendCommand", ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800194 }
195 }
196 }
197 }
198
San Mehatdeba6932010-01-20 15:14:31 -0800199 /**
200 * Issue a command to the native daemon and return the responses
201 */
San Mehat4c27e0e2010-01-29 05:22:17 -0800202 public synchronized ArrayList<String> doCommand(String cmd)
203 throws NativeDaemonConnectorException {
San Mehat67bd2cd2010-01-12 12:18:49 -0800204 sendCommand(cmd);
205
206 ArrayList<String> response = new ArrayList<String>();
207 boolean complete = false;
208 int code = -1;
209
210 while (!complete) {
211 try {
212 String line = mResponseQueue.take();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800213 if (LOCAL_LOGD) Slog.d(TAG, String.format("RSP <- {%s}", line));
San Mehat67bd2cd2010-01-12 12:18:49 -0800214 String[] tokens = line.split(" ");
215 try {
216 code = Integer.parseInt(tokens[0]);
217 } catch (NumberFormatException nfe) {
San Mehat4c27e0e2010-01-29 05:22:17 -0800218 throw new NativeDaemonConnectorException(
San Mehat67bd2cd2010-01-12 12:18:49 -0800219 String.format("Invalid response from daemon (%s)", line));
220 }
221
San Mehatec4caa02010-02-03 10:48:21 -0800222 if ((code >= 200) && (code < 600)) {
San Mehat67bd2cd2010-01-12 12:18:49 -0800223 complete = true;
San Mehatec4caa02010-02-03 10:48:21 -0800224 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800225 response.add(line);
226 } catch (InterruptedException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800227 Slog.e(TAG, "Failed to process response", ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800228 }
229 }
230
231 if (code >= ResponseCode.FailedRangeStart &&
232 code <= ResponseCode.FailedRangeEnd) {
San Mehatec4caa02010-02-03 10:48:21 -0800233 /*
234 * Note: The format of the last response in this case is
235 * "NNN <errmsg>"
236 */
237 throw new NativeDaemonConnectorException(
238 code, cmd, response.get(response.size()-1).substring(4));
San Mehat67bd2cd2010-01-12 12:18:49 -0800239 }
240 return response;
241 }
San Mehatdeba6932010-01-20 15:14:31 -0800242
243 /*
244 * Issues a list command and returns the cooked list
245 */
246 public String[] doListCommand(String cmd, int expectedResponseCode)
San Mehat4c27e0e2010-01-29 05:22:17 -0800247 throws NativeDaemonConnectorException {
San Mehatdeba6932010-01-20 15:14:31 -0800248
249 ArrayList<String> rsp = doCommand(cmd);
250 String[] rdata = new String[rsp.size()-1];
251 int idx = 0;
252
San Mehat1ff43712010-02-04 15:09:02 -0800253 for (int i = 0; i < rsp.size(); i++) {
254 String line = rsp.get(i);
San Mehatdeba6932010-01-20 15:14:31 -0800255 try {
256 String[] tok = line.split(" ");
257 int code = Integer.parseInt(tok[0]);
258 if (code == expectedResponseCode) {
San Mehat80120b42010-01-26 12:48:39 -0800259 rdata[idx++] = line.substring(tok[0].length() + 1);
San Mehatdeba6932010-01-20 15:14:31 -0800260 } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800261 if (LOCAL_LOGD) Slog.d(TAG, String.format("List terminated with {%s}", line));
San Mehat4086f752010-02-17 09:03:29 -0800262 int last = rsp.size() -1;
263 if (i != last) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800264 Slog.w(TAG, String.format("Recv'd %d lines after end of list {%s}", (last-i), cmd));
San Mehat4086f752010-02-17 09:03:29 -0800265 for (int j = i; j <= last ; j++) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800266 Slog.w(TAG, String.format("ExtraData <%s>", rsp.get(i)));
San Mehat4086f752010-02-17 09:03:29 -0800267 }
San Mehat1ff43712010-02-04 15:09:02 -0800268 }
San Mehatdeba6932010-01-20 15:14:31 -0800269 return rdata;
270 } else {
San Mehat4c27e0e2010-01-29 05:22:17 -0800271 throw new NativeDaemonConnectorException(
San Mehatdeba6932010-01-20 15:14:31 -0800272 String.format("Expected list response %d, but got %d",
273 expectedResponseCode, code));
274 }
275 } catch (NumberFormatException nfe) {
San Mehat4c27e0e2010-01-29 05:22:17 -0800276 throw new NativeDaemonConnectorException(
277 String.format("Error reading code '%s'", line));
San Mehatdeba6932010-01-20 15:14:31 -0800278 }
279 }
San Mehat4c27e0e2010-01-29 05:22:17 -0800280 throw new NativeDaemonConnectorException("Got an empty response");
San Mehatdeba6932010-01-20 15:14:31 -0800281 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800282}