1 /*
2  * Copyright (C) 2019 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 package com.android.bluetooth.avrcpcontroller;
18 
19 import android.util.Log;
20 
21 import java.io.DataOutputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 
25 import javax.obex.ClientOperation;
26 import javax.obex.ClientSession;
27 import javax.obex.HeaderSet;
28 import javax.obex.ResponseCodes;
29 
30 /**
31  * This is a base class for implementing AVRCP Controller Basic Image Profile (BIP) requests
32  */
33 abstract class BipRequest {
34     private static final String TAG = "avrcpcontroller.BipRequest";
35     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
36 
37     // User defined OBEX header identifiers
38     protected static final byte HEADER_ID_IMG_HANDLE = 0x30;
39     protected static final byte HEADER_ID_IMG_DESCRIPTOR = 0x71;
40 
41     // Request types
42     public static final int TYPE_GET_IMAGE_PROPERTIES = 0;
43     public static final int TYPE_GET_IMAGE = 1;
44 
45     protected HeaderSet mHeaderSet;
46     protected ClientOperation mOperation = null;
47     protected int mResponseCode;
48 
BipRequest()49     BipRequest() {
50         mHeaderSet = new HeaderSet();
51         mResponseCode = -1;
52     }
53 
54     /**
55      * A function that returns the type of the request.
56      *
57      * Used to determine type instead of using 'instanceof'
58      */
getType()59     public abstract int getType();
60 
61     /**
62      * A single point of entry for kicking off a AVRCP BIP request.
63      *
64      * Child classes are expected to implement this interface, filling in the details of the request
65      * (headers, operation type, error handling, etc).
66      */
execute(ClientSession session)67     public abstract void execute(ClientSession session) throws IOException;
68 
69     /**
70      * A generica GET operation, providing overridable hooks to read response headers and content.
71      */
executeGet(ClientSession session)72     protected void executeGet(ClientSession session) throws IOException {
73         debug("Exeucting GET");
74         setOperation(null);
75         try {
76             ClientOperation operation = (ClientOperation) session.get(mHeaderSet);
77             setOperation(operation);
78             operation.setGetFinalFlag(true);
79             operation.continueOperation(true, false);
80             readResponseHeaders(operation.getReceivedHeader());
81             InputStream inputStream = operation.openInputStream();
82             readResponse(inputStream);
83             inputStream.close();
84             operation.close();
85             mResponseCode = operation.getResponseCode();
86         } catch (IOException e) {
87             mResponseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
88             error("GET threw an exeception: " + e);
89             throw e;
90         }
91         debug("GET final response code is '" + mResponseCode + "'");
92     }
93 
94     /**
95      * A generica PUT operation, providing overridable hooks to read response headers.
96      */
executePut(ClientSession session, byte[] body)97     protected void executePut(ClientSession session, byte[] body) throws IOException {
98         debug("Exeucting PUT");
99         setOperation(null);
100         mHeaderSet.setHeader(HeaderSet.LENGTH, Long.valueOf(body.length));
101         try {
102             ClientOperation operation = (ClientOperation) session.put(mHeaderSet);
103             setOperation(operation);
104             DataOutputStream outputStream = mOperation.openDataOutputStream();
105             outputStream.write(body);
106             outputStream.close();
107             readResponseHeaders(operation.getReceivedHeader());
108             operation.close();
109             mResponseCode = operation.getResponseCode();
110         } catch (IOException e) {
111             mResponseCode = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
112             error("PUT threw an exeception: " + e);
113             throw e;
114         }
115         debug("PUT final response code is '" + mResponseCode + "'");
116     }
117 
118     /**
119      * Determine if the request was a success
120      *
121      * @return True if the request was successful, false otherwise
122      */
isSuccess()123     public final boolean isSuccess() {
124         return (mResponseCode == ResponseCodes.OBEX_HTTP_OK);
125     }
126 
127     /**
128      * Get the actual response code associated with the request
129      *
130      * @return The response code as in integer
131      */
getResponseCode()132     public final int getResponseCode() {
133         return mResponseCode;
134     }
135 
136     /**
137      * A callback for subclasses to add logic to make determinations against the content of the
138      * returned headers.
139      */
readResponseHeaders(HeaderSet headerset)140     protected void readResponseHeaders(HeaderSet headerset) {
141         /* nothing here by default */
142     }
143 
144     /**
145      * A callback for subclasses to add logic to make determinations against the content of the
146      * returned response body.
147      */
readResponse(InputStream stream)148     protected void readResponse(InputStream stream) throws IOException {
149         /* nothing here by default */
150     }
151 
getOperation()152     private synchronized ClientOperation getOperation() {
153         return mOperation;
154     }
155 
setOperation(ClientOperation operation)156     private synchronized void setOperation(ClientOperation operation) {
157         mOperation = operation;
158     }
159 
160     @Override
toString()161     public String toString() {
162         return TAG + " (type: " + getType() + ", mResponseCode: " + mResponseCode + ")";
163     }
164 
165     /**
166      * Print to debug if debug is enabled for this class
167      */
debug(String msg)168     protected void debug(String msg) {
169         if (DBG) {
170             Log.d(TAG, msg);
171         }
172     }
173 
174     /**
175      * Print to warn
176      */
warn(String msg)177     protected void warn(String msg) {
178         Log.w(TAG, msg);
179     }
180 
181     /**
182      * Print to error
183      */
error(String msg)184     protected void error(String msg) {
185         Log.e(TAG, msg);
186     }
187 }
188