blob: 98e00dc11fa9afb915aac43c639af4a02d4b900c [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;
24import android.util.Config;
25import android.util.Log;
26
27import java.io.IOException;
28import java.io.InputStream;
29import java.io.OutputStream;
30import java.net.Socket;
31import java.lang.IllegalStateException;
32
33import java.util.List;
34import java.util.ArrayList;
35import java.util.ListIterator;
36import java.util.concurrent.BlockingQueue;
37import java.util.concurrent.LinkedBlockingQueue;
38
39/**
40 * Generic connector class for interfacing with a native
41 * daemon which uses the libsysutils FrameworkListener
42 * protocol.
43 */
44final class NativeDaemonConnector implements Runnable {
45
46 private BlockingQueue<String> mResponseQueue;
47 private OutputStream mOutputStream;
48 private String TAG = "NativeDaemonConnector";
49 private String mSocket;
50 private INativeDaemonConnectorCallbacks mCallbacks;
51
52 class ResponseCode {
53 public static final int ActionInitiated = 100;
54
55 public static final int CommandOkay = 200;
56
57 // The range of 400 -> 599 is reserved for cmd failures
58 public static final int OperationFailed = 400;
59 public static final int CommandSyntaxError = 500;
60 public static final int CommandParameterError = 501;
61
62 public static final int UnsolicitedInformational = 600;
63
64 //
65 public static final int FailedRangeStart = 400;
66 public static final int FailedRangeEnd = 599;
67 }
68
69 NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks,
70 String socket, int responseQueueSize, String logTag) {
71 mCallbacks = callbacks;
72 if (logTag != null)
73 TAG = logTag;
74 mSocket = socket;
75 mResponseQueue = new LinkedBlockingQueue<String>(responseQueueSize);
76 }
77
78 public void run() {
79
80 while (true) {
81 try {
82 listenToSocket();
83 } catch (Exception e) {
84 Log.e(TAG, "Error in NativeDaemonConnector", e);
85 SystemClock.sleep(1000);
86 }
87 }
88 }
89
90 private void listenToSocket() {
91 LocalSocket socket = null;
92
93 try {
94 socket = new LocalSocket();
95 LocalSocketAddress address = new LocalSocketAddress(mSocket,
96 LocalSocketAddress.Namespace.RESERVED);
97
98 socket.connect(address);
99 mCallbacks.onDaemonConnected();
100
101 InputStream inputStream = socket.getInputStream();
102 mOutputStream = socket.getOutputStream();
103
104 byte[] buffer = new byte[4096];
105
106 while (true) {
107 int count = inputStream.read(buffer);
108 if (count < 0) break;
109
110 int start = 0;
111 for (int i = 0; i < count; i++) {
112 if (buffer[i] == 0) {
113 String event = new String(buffer, start, i - start);
114// Log.d(TAG, "Got packet {" + event + "}");
115
116 String[] tokens = event.split(" ");
117 try {
118 int code = Integer.parseInt(tokens[0]);
119
120 if (code >= ResponseCode.UnsolicitedInformational) {
121 try {
122 if (!mCallbacks.onEvent(code, event, tokens)) {
123 Log.w(TAG, String.format(
124 "Unhandled event (%s)", event));
125 }
126 } catch (Exception ex) {
127 Log.e(TAG, String.format(
128 "Error handling '%s'", event), ex);
129 }
130 } else {
131 try {
132 mResponseQueue.put(event);
133 } catch (InterruptedException ex) {
134 Log.e(TAG, "Failed to put response onto queue", ex);
135 }
136 }
137 } catch (NumberFormatException nfe) {
138 Log.w(TAG, String.format("Bad msg (%s)", event));
139 }
140 start = i + 1;
141 }
142 }
143 }
144 } catch (IOException ex) {
145 Log.e(TAG, "Communications error", ex);
146 }
147
148 synchronized (this) {
149 if (mOutputStream != null) {
150 try {
151 mOutputStream.close();
152 } catch (IOException e) {
153 Log.w(TAG, "Failed closing output stream", e);
154 }
155
156 mOutputStream = null;
157 }
158 }
159
160 try {
161 if (socket != null) {
162 socket.close();
163 }
164 } catch (IOException ex) {
165 Log.w(TAG, "Failed closing socket", ex);
166 }
167
168 Log.e(TAG, "Failed to connect to native daemon",
169 new IllegalStateException());
170 SystemClock.sleep(5000);
171 }
172
173 private void sendCommand(String command) {
174 sendCommand(command, null);
175 }
176
177 /**
178 * Sends a command to the daemon with a single argument
179 *
180 * @param command The command to send to the daemon
181 * @param argument The argument to send with the command (or null)
182 */
183 private void sendCommand(String command, String argument) {
184 synchronized (this) {
185 Log.d(TAG, "sendCommand {" + command + "} {" + argument + "}");
186 if (mOutputStream == null) {
187 Log.e(TAG, "No connection to daemon", new IllegalStateException());
188 } else {
189 StringBuilder builder = new StringBuilder(command);
190 if (argument != null) {
191 builder.append(argument);
192 }
193 builder.append('\0');
194
195 try {
196 mOutputStream.write(builder.toString().getBytes());
197 } catch (IOException ex) {
198 Log.e(TAG, "IOException in sendCommand", ex);
199 }
200 }
201 }
202 }
203
San Mehatdeba6932010-01-20 15:14:31 -0800204 /**
205 * Issue a command to the native daemon and return the responses
206 */
San Mehat67bd2cd2010-01-12 12:18:49 -0800207 public synchronized ArrayList<String> doCommand(String cmd) throws IllegalStateException {
208 sendCommand(cmd);
209
210 ArrayList<String> response = new ArrayList<String>();
211 boolean complete = false;
212 int code = -1;
213
214 while (!complete) {
215 try {
216 String line = mResponseQueue.take();
217// Log.d(TAG, "Removed off queue -> " + line);
218 String[] tokens = line.split(" ");
219 try {
220 code = Integer.parseInt(tokens[0]);
221 } catch (NumberFormatException nfe) {
222 throw new IllegalStateException(
223 String.format("Invalid response from daemon (%s)", line));
224 }
225
226 if ((code >= 200) && (code < 600))
227 complete = true;
228 response.add(line);
229 } catch (InterruptedException ex) {
230 Log.e(TAG, "InterruptedException");
231 }
232 }
233
234 if (code >= ResponseCode.FailedRangeStart &&
235 code <= ResponseCode.FailedRangeEnd) {
236 throw new IllegalStateException(String.format(
237 "Command %s failed with code %d",
238 cmd, code));
239 }
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)
247 throws IllegalStateException {
248
249 ArrayList<String> rsp = doCommand(cmd);
250 String[] rdata = new String[rsp.size()-1];
251 int idx = 0;
252
253 for (String line : rsp) {
254 try {
255 String[] tok = line.split(" ");
256 int code = Integer.parseInt(tok[0]);
257 if (code == expectedResponseCode) {
258 if (tok.length !=2) {
259 throw new IllegalStateException(
260 String.format("Malformatted list entry '%s'", line));
261 }
262 rdata[idx++] = tok[1];
263 } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
264 return rdata;
265 } else {
266 throw new IllegalStateException(
267 String.format("Expected list response %d, but got %d",
268 expectedResponseCode, code));
269 }
270 } catch (NumberFormatException nfe) {
271 throw new IllegalStateException(String.format("Error reading code '%s'", line));
272 }
273 }
274 throw new IllegalStateException("Got an empty response");
275 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800276}