1/*
2 * Copyright (C) 2010, 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.communication;
18
19import android.util.Log;
20
21import com.android.internal.communication.MsgHeader;
22import com.android.internal.telephony.RilChannel;
23import com.google.protobuf.micro.InvalidProtocolBufferMicroException;
24import com.google.protobuf.micro.MessageMicro;
25
26import java.io.IOException;
27import java.nio.ByteBuffer;
28import java.nio.ByteOrder;
29
30/**
31 * A message
32 */
33public class Msg {
34    private MsgHeader  mHeader;
35    private ByteBuffer mData;
36
37    /**
38     * Send a message header
39     *
40     * @param mh is message header to write
41     * @throws IOException
42     */
43    private static void sendHeader(RilChannel rc, MsgHeader mh) throws IOException {
44        ByteBuffer lenBuffer = ByteBuffer.allocateDirect(4);
45        lenBuffer.order(ByteOrder.LITTLE_ENDIAN);
46        lenBuffer.putInt(mh.getSerializedSize());
47
48        ByteBuffer mhBuffer = ByteBuffer.allocateDirect(mh.getCachedSize());
49        mhBuffer.put(mh.toByteArray());
50
51        rc.rewindSendAll(lenBuffer);
52        rc.rewindSendAll(mhBuffer);
53    }
54
55    /**
56     * Read a message header
57     *
58     * @returns message header
59     * @throws IOException
60     */
61    private static MsgHeader recvHeader(RilChannel rc) throws IOException {
62        ByteBuffer lenBuffer = ByteBuffer.allocate(4);
63        lenBuffer.order(ByteOrder.LITTLE_ENDIAN);
64        int lenRead = rc.recvAllRewind(lenBuffer);
65        int lenHeader = lenBuffer.getInt();
66
67        ByteBuffer mhBuffer = ByteBuffer.allocate(lenHeader);
68        lenRead = rc.recvAllRewind(mhBuffer);
69        MsgHeader mh = MsgHeader.parseFrom(mhBuffer.array());
70        return mh;
71    }
72
73    /**
74     * Msg Constructor
75     */
76    private Msg() {
77    }
78
79    /**
80     * Get a message
81     */
82    public static Msg obtain() {
83        // TODO: Get from a free list
84        return new Msg();
85    }
86
87    /**
88     * Release a message
89     */
90    public void release() {
91        // TODO: place back on free list
92    }
93
94    /**
95     * Send a message header followed by the data if present
96     *
97     * The length data field will be filled in as appropriate
98     * @param mh header
99     * @param data if not null and length > 0 sent after header
100     * @throws IOException
101     */
102    public static final void send(RilChannel rc, MsgHeader mh, ByteBuffer data)
103            throws IOException {
104        int lenData;
105
106        if (data == null) {
107            lenData = 0;
108        } else {
109            data.rewind();
110            lenData = data.remaining();
111        }
112        mh.setLengthData(lenData);
113        sendHeader(rc, mh);
114        if (lenData > 0) {
115            rc.sendAll(data);
116        }
117    }
118
119    /**
120     * Send a message with cmd, token, status followed by the data.
121     *
122     * The length data field will be filled in as appropriate
123     * @param cmd for the header
124     * @param token for the header
125     * @param status for the header
126     * @param pb is the protobuf to send
127     * @throws IOException
128     */
129    public static final void send(RilChannel rc, int cmd, long token, int status, MessageMicro pb)
130            throws IOException {
131        MsgHeader mh = new MsgHeader();
132        mh.setCmd(cmd);
133        mh.setToken(token);
134        mh.setStatus(status);
135
136        ByteBuffer data;
137        if (pb != null) {
138            data = ByteBuffer.wrap(pb.toByteArray());
139        } else {
140            data = null;
141        }
142        send(rc, mh, data);
143    }
144
145    /**
146     * Send a message with cmd, token, status followed by the data.
147     *
148     * The length data field will be filled in as appropriate
149     * @param cmd for the header
150     * @param token for the header
151     * @param pb is the protobuf to send
152     * @throws IOException
153     */
154    public static final void send(RilChannel rc, int cmd, long token, MessageMicro pb)
155            throws IOException {
156        send(rc, cmd, token, 0, pb);
157    }
158
159    /**
160     * Send a message with cmd followed by the data.
161     *
162     * The length data field will be filled in as appropriate
163     * @param cmd for the header
164     * @param pb is the protobuf to send
165     * @throws IOException
166     */
167    public static final void send(RilChannel rc, int cmd, MessageMicro pb) throws IOException {
168        send(rc, cmd, 0, 0, pb);
169    }
170
171    /**
172     * Send a message with cmd, token and status but no data
173     *
174     * The length data field will be filled in as appropriate
175     * @param cmd for the header
176     * @param token for the header
177     * @param status for the header
178     * @throws IOException
179     */
180    public static final void send(RilChannel rc, int cmd, long token, int status)
181            throws IOException {
182        send(rc, cmd, token, status, null);
183    }
184
185    /**
186     * Send a message with cmd and token but no data
187     *
188     * The length data field will be filled in as appropriate
189     * @param cmd for the header
190     * @param token for the header
191     * @throws IOException
192     */
193    public static final void send(RilChannel rc, int cmd, long token) throws IOException {
194        send(rc, cmd, token, 0, null);
195    }
196
197    /**
198     * Send a message with cmd but no data
199     *
200     * The length data field will be filled in as appropriate
201     * @param cmd for the header
202     * @throws IOException
203     */
204    public static final void send(RilChannel rc, int cmd) throws IOException {
205        send(rc, cmd, 0, 0, null);
206    }
207
208    /**
209     * Read a message
210     *
211     * @return Msg
212     * @throws IOException
213     */
214    public static final Msg recv(RilChannel rc) throws IOException {
215        Msg msg = Msg.obtain();
216        msg.read(rc);
217        return msg;
218    }
219
220    /**
221     * Read a message header and data.
222     *
223     * @throws IOException
224     */
225    public void read(RilChannel rc) throws IOException {
226        mHeader = recvHeader(rc);
227        if (mHeader.getLengthData() > 0) {
228            ByteBuffer bb = ByteBuffer.allocate(mHeader.getLengthData());
229            rc.recvAllRewind(bb);
230            mData = bb;
231        }
232    }
233
234    /**
235     * Print the message header.
236     *
237     * @param tag for the header
238     */
239    public void printHeader(String tag) {
240        Log.d(tag, " cmd=" + mHeader.getCmd() + " token=" + mHeader.getToken() + " status="
241                        + mHeader.getStatus() + " lengthData=" + mHeader.getLengthData());
242    }
243
244    /**
245     * Set data (for testing purposes only).
246     */
247    public void setData(ByteBuffer data) {
248        mData = data;
249    }
250
251    /**
252     * Set header (for testing purposes only).
253     */
254    public void setHeader(MsgHeader header) {
255        mHeader = header;
256    }
257
258    /**
259     * @return cmd
260     */
261    public int getCmd() {
262        return mHeader.getCmd();
263    }
264
265    /**
266     * @return token
267     */
268    public long getToken() {
269        return mHeader.getToken();
270    }
271
272    /**
273     * @return status
274     */
275    public int getStatus() {
276        return mHeader.getStatus();
277    }
278
279    /**
280     * @return data ByteBuffer
281     */
282    public ByteBuffer getData() {
283        return mData;
284    }
285
286    /**
287     * @return data at index
288     */
289    public byte getData(int index) {
290        return mData.get(index);
291    }
292
293    /**
294     * Return data as a Class<T>.
295     *
296     * @param <T> a class that extends MessageMicro.
297     * @param c the T.class to create from the data.
298     * @param data is the MessageMicro protobuf to be converted.
299     * @return null if an error occurs.
300     */
301    @SuppressWarnings("unchecked")
302    public static final <T extends MessageMicro> T getAs(Class<T> c, byte[] data) {
303        Object o = null;
304        if ((data != null) && (data.length > 0)) {
305            try {
306                o = c.newInstance().mergeFrom(data);
307            } catch (InvalidProtocolBufferMicroException e) {
308                e.printStackTrace();
309            } catch (InstantiationException e) {
310                e.printStackTrace();
311            } catch (IllegalAccessException e) {
312                e.printStackTrace();
313            }
314        }
315        return (T)o;
316    }
317
318    /**
319     * Return data as a Class<T>.
320     *
321     * @param <T> a class that extends MessageMicro.
322     * @param c the T.class to create from data.
323     * @return null if an error occurs
324     */
325    @SuppressWarnings("unchecked")
326    public <T extends MessageMicro> T getDataAs(Class<T> c) {
327        Object o;
328
329        if ((mData != null) && (mData.remaining() > 0)) {
330            o = getAs(c, mData.array());
331        } else {
332            o = null;
333        }
334        return (T)o;
335    }
336}
337