1/*
2 * Copyright (C) 2013 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
17//#define LOG_NDEBUG 0
18#define LOG_TAG "IMediaHTTPConnection"
19#include <utils/Log.h>
20
21#include <media/IMediaHTTPConnection.h>
22
23#include <binder/IMemory.h>
24#include <binder/Parcel.h>
25#include <utils/String8.h>
26#include <media/stagefright/foundation/ADebug.h>
27#include <media/stagefright/MediaErrors.h>
28
29namespace android {
30
31enum {
32    CONNECT = IBinder::FIRST_CALL_TRANSACTION,
33    DISCONNECT,
34    READ_AT,
35    GET_SIZE,
36    GET_MIME_TYPE,
37    GET_URI
38};
39
40struct BpMediaHTTPConnection : public BpInterface<IMediaHTTPConnection> {
41    BpMediaHTTPConnection(const sp<IBinder> &impl)
42        : BpInterface<IMediaHTTPConnection>(impl) {
43    }
44
45    virtual bool connect(
46            const char *uri, const KeyedVector<String8, String8> *headers) {
47        Parcel data, reply;
48        data.writeInterfaceToken(
49                IMediaHTTPConnection::getInterfaceDescriptor());
50
51        String16 tmp(uri);
52        data.writeString16(tmp);
53
54        tmp = String16("");
55        if (headers != NULL) {
56            for (size_t i = 0; i < headers->size(); ++i) {
57                String16 key(headers->keyAt(i).string());
58                String16 val(headers->valueAt(i).string());
59
60                tmp.append(key);
61                tmp.append(String16(": "));
62                tmp.append(val);
63                tmp.append(String16("\r\n"));
64            }
65        }
66        data.writeString16(tmp);
67
68        remote()->transact(CONNECT, data, &reply);
69
70        int32_t exceptionCode = reply.readExceptionCode();
71
72        if (exceptionCode) {
73            return false;
74        }
75
76        sp<IBinder> binder = reply.readStrongBinder();
77        mMemory = interface_cast<IMemory>(binder);
78
79        return mMemory != NULL;
80    }
81
82    virtual void disconnect() {
83        Parcel data, reply;
84        data.writeInterfaceToken(
85                IMediaHTTPConnection::getInterfaceDescriptor());
86
87        remote()->transact(DISCONNECT, data, &reply);
88    }
89
90    virtual ssize_t readAt(off64_t offset, void *buffer, size_t size) {
91        Parcel data, reply;
92        data.writeInterfaceToken(
93                IMediaHTTPConnection::getInterfaceDescriptor());
94
95        data.writeInt64(offset);
96        data.writeInt32(size);
97
98        status_t err = remote()->transact(READ_AT, data, &reply);
99        if (err != OK) {
100            ALOGE("remote readAt failed");
101            return UNKNOWN_ERROR;
102        }
103
104        int32_t exceptionCode = reply.readExceptionCode();
105
106        if (exceptionCode) {
107            return UNKNOWN_ERROR;
108        }
109
110        int32_t lenOrErrorCode = reply.readInt32();
111
112        // Negative values are error codes
113        if (lenOrErrorCode < 0) {
114            return lenOrErrorCode;
115        }
116
117        size_t len = lenOrErrorCode;
118
119        if (len > size) {
120            ALOGE("requested %zu, got %zu", size, len);
121            return ERROR_OUT_OF_RANGE;
122        }
123        if (len > mMemory->size()) {
124            ALOGE("got %zu, but memory has %zu", len, mMemory->size());
125            return ERROR_OUT_OF_RANGE;
126        }
127
128        memcpy(buffer, mMemory->pointer(), len);
129
130        return len;
131    }
132
133    virtual off64_t getSize() {
134        Parcel data, reply;
135        data.writeInterfaceToken(
136                IMediaHTTPConnection::getInterfaceDescriptor());
137
138        remote()->transact(GET_SIZE, data, &reply);
139
140        int32_t exceptionCode = reply.readExceptionCode();
141
142        if (exceptionCode) {
143            return UNKNOWN_ERROR;
144        }
145
146        return reply.readInt64();
147    }
148
149    virtual status_t getMIMEType(String8 *mimeType) {
150        *mimeType = String8("");
151
152        Parcel data, reply;
153        data.writeInterfaceToken(
154                IMediaHTTPConnection::getInterfaceDescriptor());
155
156        remote()->transact(GET_MIME_TYPE, data, &reply);
157
158        int32_t exceptionCode = reply.readExceptionCode();
159
160        if (exceptionCode) {
161            return UNKNOWN_ERROR;
162        }
163
164        *mimeType = String8(reply.readString16());
165
166        return OK;
167    }
168
169    virtual status_t getUri(String8 *uri) {
170        *uri = String8("");
171
172        Parcel data, reply;
173        data.writeInterfaceToken(
174                IMediaHTTPConnection::getInterfaceDescriptor());
175
176        remote()->transact(GET_URI, data, &reply);
177
178        int32_t exceptionCode = reply.readExceptionCode();
179
180        if (exceptionCode) {
181            return UNKNOWN_ERROR;
182        }
183
184        *uri = String8(reply.readString16());
185
186        return OK;
187    }
188
189private:
190    sp<IMemory> mMemory;
191};
192
193IMPLEMENT_META_INTERFACE(
194        MediaHTTPConnection, "android.media.IMediaHTTPConnection");
195
196} // namespace android
197