blob: 88d94c2c10ea1933d3bf447d382c421d1237758a [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
Kenny Root961aa8c2010-03-22 18:02:45 -070051 private final int BUFFER_SIZE = 4096;
52
San Mehat67bd2cd2010-01-12 12:18:49 -080053 class ResponseCode {
54 public static final int ActionInitiated = 100;
55
56 public static final int CommandOkay = 200;
57
58 // The range of 400 -> 599 is reserved for cmd failures
59 public static final int OperationFailed = 400;
60 public static final int CommandSyntaxError = 500;
61 public static final int CommandParameterError = 501;
62
63 public static final int UnsolicitedInformational = 600;
64
65 //
66 public static final int FailedRangeStart = 400;
67 public static final int FailedRangeEnd = 599;
68 }
69
70 NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks,
71 String socket, int responseQueueSize, String logTag) {
72 mCallbacks = callbacks;
73 if (logTag != null)
74 TAG = logTag;
75 mSocket = socket;
76 mResponseQueue = new LinkedBlockingQueue<String>(responseQueueSize);
77 }
78
79 public void run() {
80
81 while (true) {
82 try {
83 listenToSocket();
84 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -080085 Slog.e(TAG, "Error in NativeDaemonConnector", e);
San Mehat4c27e0e2010-01-29 05:22:17 -080086 SystemClock.sleep(5000);
San Mehat67bd2cd2010-01-12 12:18:49 -080087 }
88 }
89 }
90
San Mehat4c27e0e2010-01-29 05:22:17 -080091 private void listenToSocket() throws IOException {
Kenny Root961aa8c2010-03-22 18:02:45 -070092 LocalSocket socket = null;
San Mehat67bd2cd2010-01-12 12:18:49 -080093
94 try {
95 socket = new LocalSocket();
96 LocalSocketAddress address = new LocalSocketAddress(mSocket,
97 LocalSocketAddress.Namespace.RESERVED);
98
99 socket.connect(address);
San Mehat67bd2cd2010-01-12 12:18:49 -0800100
101 InputStream inputStream = socket.getInputStream();
102 mOutputStream = socket.getOutputStream();
103
anga030bc882011-02-01 14:10:25 +0100104 mCallbacks.onDaemonConnected();
105
Kenny Root961aa8c2010-03-22 18:02:45 -0700106 byte[] buffer = new byte[BUFFER_SIZE];
107 int start = 0;
San Mehat67bd2cd2010-01-12 12:18:49 -0800108
109 while (true) {
Kenny Root961aa8c2010-03-22 18:02:45 -0700110 int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
San Mehat67bd2cd2010-01-12 12:18:49 -0800111 if (count < 0) break;
112
Kenny Root12da9d72010-09-02 22:18:14 -0700113 // Add our starting point to the count and reset the start.
114 count += start;
115 start = 0;
116
San Mehat67bd2cd2010-01-12 12:18:49 -0800117 for (int i = 0; i < count; i++) {
118 if (buffer[i] == 0) {
119 String event = new String(buffer, start, i - start);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800120 if (LOCAL_LOGD) Slog.d(TAG, String.format("RCV <- {%s}", event));
San Mehat67bd2cd2010-01-12 12:18:49 -0800121
122 String[] tokens = event.split(" ");
123 try {
124 int code = Integer.parseInt(tokens[0]);
125
126 if (code >= ResponseCode.UnsolicitedInformational) {
127 try {
128 if (!mCallbacks.onEvent(code, event, tokens)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800129 Slog.w(TAG, String.format(
San Mehat67bd2cd2010-01-12 12:18:49 -0800130 "Unhandled event (%s)", event));
131 }
132 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800133 Slog.e(TAG, String.format(
San Mehat67bd2cd2010-01-12 12:18:49 -0800134 "Error handling '%s'", event), ex);
135 }
Irfan Sheriff1cd94ef2011-01-16 14:31:55 -0800136 } else {
137 try {
138 mResponseQueue.put(event);
139 } catch (InterruptedException ex) {
140 Slog.e(TAG, "Failed to put response onto queue", ex);
141 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800142 }
143 } catch (NumberFormatException nfe) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800144 Slog.w(TAG, String.format("Bad msg (%s)", event));
San Mehat67bd2cd2010-01-12 12:18:49 -0800145 }
146 start = i + 1;
147 }
148 }
Kenny Root12da9d72010-09-02 22:18:14 -0700149
150 // We should end at the amount we read. If not, compact then
151 // buffer and read again.
Kenny Root961aa8c2010-03-22 18:02:45 -0700152 if (start != count) {
153 final int remaining = BUFFER_SIZE - start;
154 System.arraycopy(buffer, start, buffer, 0, remaining);
155 start = remaining;
156 } else {
157 start = 0;
158 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800159 }
160 } catch (IOException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800161 Slog.e(TAG, "Communications error", ex);
San Mehat4c27e0e2010-01-29 05:22:17 -0800162 throw ex;
163 } finally {
164 synchronized (this) {
165 if (mOutputStream != null) {
166 try {
167 mOutputStream.close();
168 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800169 Slog.w(TAG, "Failed closing output stream", e);
San Mehat4c27e0e2010-01-29 05:22:17 -0800170 }
171 mOutputStream = null;
San Mehat67bd2cd2010-01-12 12:18:49 -0800172 }
San Mehat4c27e0e2010-01-29 05:22:17 -0800173 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800174
San Mehat4c27e0e2010-01-29 05:22:17 -0800175 try {
176 if (socket != null) {
177 socket.close();
178 }
179 } catch (IOException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800180 Slog.w(TAG, "Failed closing socket", ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800181 }
182 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800183 }
184
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700185 private void sendCommand(String command)
186 throws NativeDaemonConnectorException {
San Mehat67bd2cd2010-01-12 12:18:49 -0800187 sendCommand(command, null);
188 }
189
190 /**
191 * Sends a command to the daemon with a single argument
192 *
193 * @param command The command to send to the daemon
194 * @param argument The argument to send with the command (or null)
195 */
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700196 private void sendCommand(String command, String argument)
197 throws NativeDaemonConnectorException {
San Mehat67bd2cd2010-01-12 12:18:49 -0800198 synchronized (this) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800199 if (LOCAL_LOGD) Slog.d(TAG, String.format("SND -> {%s} {%s}", command, argument));
San Mehat67bd2cd2010-01-12 12:18:49 -0800200 if (mOutputStream == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800201 Slog.e(TAG, "No connection to daemon", new IllegalStateException());
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700202 throw new NativeDaemonConnectorException("No output stream!");
San Mehat67bd2cd2010-01-12 12:18:49 -0800203 } else {
204 StringBuilder builder = new StringBuilder(command);
205 if (argument != null) {
206 builder.append(argument);
207 }
208 builder.append('\0');
209
210 try {
211 mOutputStream.write(builder.toString().getBytes());
212 } catch (IOException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800213 Slog.e(TAG, "IOException in sendCommand", ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800214 }
215 }
216 }
217 }
218
San Mehatdeba6932010-01-20 15:14:31 -0800219 /**
220 * Issue a command to the native daemon and return the responses
221 */
San Mehat4c27e0e2010-01-29 05:22:17 -0800222 public synchronized ArrayList<String> doCommand(String cmd)
223 throws NativeDaemonConnectorException {
Irfan Sheriff1cd94ef2011-01-16 14:31:55 -0800224 mResponseQueue.clear();
San Mehat67bd2cd2010-01-12 12:18:49 -0800225 sendCommand(cmd);
226
227 ArrayList<String> response = new ArrayList<String>();
228 boolean complete = false;
229 int code = -1;
230
231 while (!complete) {
232 try {
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700233 // TODO - this should not block forever
San Mehat67bd2cd2010-01-12 12:18:49 -0800234 String line = mResponseQueue.take();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800235 if (LOCAL_LOGD) Slog.d(TAG, String.format("RSP <- {%s}", line));
San Mehat67bd2cd2010-01-12 12:18:49 -0800236 String[] tokens = line.split(" ");
237 try {
238 code = Integer.parseInt(tokens[0]);
239 } catch (NumberFormatException nfe) {
San Mehat4c27e0e2010-01-29 05:22:17 -0800240 throw new NativeDaemonConnectorException(
San Mehat67bd2cd2010-01-12 12:18:49 -0800241 String.format("Invalid response from daemon (%s)", line));
242 }
243
San Mehatec4caa02010-02-03 10:48:21 -0800244 if ((code >= 200) && (code < 600)) {
San Mehat67bd2cd2010-01-12 12:18:49 -0800245 complete = true;
San Mehatec4caa02010-02-03 10:48:21 -0800246 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800247 response.add(line);
248 } catch (InterruptedException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800249 Slog.e(TAG, "Failed to process response", ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800250 }
251 }
252
253 if (code >= ResponseCode.FailedRangeStart &&
254 code <= ResponseCode.FailedRangeEnd) {
San Mehatec4caa02010-02-03 10:48:21 -0800255 /*
256 * Note: The format of the last response in this case is
257 * "NNN <errmsg>"
258 */
259 throw new NativeDaemonConnectorException(
260 code, cmd, response.get(response.size()-1).substring(4));
San Mehat67bd2cd2010-01-12 12:18:49 -0800261 }
262 return response;
263 }
San Mehatdeba6932010-01-20 15:14:31 -0800264
265 /*
266 * Issues a list command and returns the cooked list
267 */
268 public String[] doListCommand(String cmd, int expectedResponseCode)
San Mehat4c27e0e2010-01-29 05:22:17 -0800269 throws NativeDaemonConnectorException {
San Mehatdeba6932010-01-20 15:14:31 -0800270
271 ArrayList<String> rsp = doCommand(cmd);
272 String[] rdata = new String[rsp.size()-1];
273 int idx = 0;
274
San Mehat1ff43712010-02-04 15:09:02 -0800275 for (int i = 0; i < rsp.size(); i++) {
276 String line = rsp.get(i);
San Mehatdeba6932010-01-20 15:14:31 -0800277 try {
278 String[] tok = line.split(" ");
279 int code = Integer.parseInt(tok[0]);
280 if (code == expectedResponseCode) {
San Mehat80120b42010-01-26 12:48:39 -0800281 rdata[idx++] = line.substring(tok[0].length() + 1);
San Mehatdeba6932010-01-20 15:14:31 -0800282 } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800283 if (LOCAL_LOGD) Slog.d(TAG, String.format("List terminated with {%s}", line));
San Mehat4086f752010-02-17 09:03:29 -0800284 int last = rsp.size() -1;
285 if (i != last) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800286 Slog.w(TAG, String.format("Recv'd %d lines after end of list {%s}", (last-i), cmd));
San Mehat4086f752010-02-17 09:03:29 -0800287 for (int j = i; j <= last ; j++) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800288 Slog.w(TAG, String.format("ExtraData <%s>", rsp.get(i)));
San Mehat4086f752010-02-17 09:03:29 -0800289 }
San Mehat1ff43712010-02-04 15:09:02 -0800290 }
San Mehatdeba6932010-01-20 15:14:31 -0800291 return rdata;
292 } else {
San Mehat4c27e0e2010-01-29 05:22:17 -0800293 throw new NativeDaemonConnectorException(
San Mehatdeba6932010-01-20 15:14:31 -0800294 String.format("Expected list response %d, but got %d",
295 expectedResponseCode, code));
296 }
297 } catch (NumberFormatException nfe) {
San Mehat4c27e0e2010-01-29 05:22:17 -0800298 throw new NativeDaemonConnectorException(
299 String.format("Error reading code '%s'", line));
San Mehatdeba6932010-01-20 15:14:31 -0800300 }
301 }
San Mehat4c27e0e2010-01-29 05:22:17 -0800302 throw new NativeDaemonConnectorException("Got an empty response");
San Mehatdeba6932010-01-20 15:14:31 -0800303 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800304}