1 /*
2 * Copyright (C) 2013 Samsung System LSI
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 package com.android.bluetooth.map;
16 
17 import android.util.Log;
18 import android.util.Xml;
19 
20 import com.android.bluetooth.DeviceWorkArounds;
21 import com.android.internal.util.FastXmlSerializer;
22 
23 import org.xmlpull.v1.XmlSerializer;
24 
25 import java.io.IOException;
26 import java.io.StringWriter;
27 import java.io.UnsupportedEncodingException;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31 
32 public class BluetoothMapMessageListing {
33     private boolean mHasUnread = false;
34     private static final String TAG = "BluetoothMapMessageListing";
35     private static final boolean D = BluetoothMapService.DEBUG;
36 
37     private List<BluetoothMapMessageListingElement> mList;
38 
BluetoothMapMessageListing()39     public BluetoothMapMessageListing() {
40         mList = new ArrayList<BluetoothMapMessageListingElement>();
41     }
42 
add(BluetoothMapMessageListingElement element)43     public void add(BluetoothMapMessageListingElement element) {
44         mList.add(element);
45         /* update info regarding whether the list contains unread messages */
46         if (!element.getReadBool()) {
47             mHasUnread = true;
48         }
49     }
50 
51     /**
52      * Used to fetch the number of BluetoothMapMessageListingElement elements in the list.
53      * @return the number of elements in the list.
54      */
getCount()55     public int getCount() {
56         if (mList != null) {
57             return mList.size();
58         }
59         return 0;
60     }
61 
62     /**
63      * does the list contain any unread messages
64      * @return true if unread messages have been added to the list, else false
65      */
hasUnread()66     public boolean hasUnread() {
67         return mHasUnread;
68     }
69 
70 
71     /**
72      *  returns the entire list as a list
73      * @return list
74      */
getList()75     public List<BluetoothMapMessageListingElement> getList() {
76         return mList;
77     }
78 
79     /**
80      * Encode the list of BluetoothMapMessageListingElement(s) into a UTF-8
81      * formatted XML-string in a trimmed byte array
82      *
83      * @param version the version as a string.
84      *        Set the listing version to e.g. "1.0" or "1.1".
85      *        To make this future proof, no check is added to validate the value, hence be careful.
86      * @return a reference to the encoded byte array.
87      * @throws UnsupportedEncodingException
88      *             if UTF-8 encoding is unsupported on the platform.
89      */
90     // TODO: Remove includeThreadId when MAP-IM is adopted
encode(boolean includeThreadId, String version)91     public byte[] encode(boolean includeThreadId, String version)
92             throws UnsupportedEncodingException {
93         StringWriter sw = new StringWriter();
94         XmlSerializer xmlMsgElement = null;
95         boolean isBenzCarkit = DeviceWorkArounds.addressStartsWith(
96                 BluetoothMapService.getRemoteDevice().getAddress(),
97                 DeviceWorkArounds.MERCEDES_BENZ_CARKIT);
98         try {
99             if (isBenzCarkit) {
100                 Log.d(TAG, "java_interop: Remote is Mercedes Benz, "
101                         + "using Xml Workaround.");
102                 xmlMsgElement = Xml.newSerializer();
103                 xmlMsgElement.setOutput(sw);
104                 xmlMsgElement.text("\n");
105             } else {
106                 xmlMsgElement = new FastXmlSerializer(0);
107                 xmlMsgElement.setOutput(sw);
108                 xmlMsgElement.startDocument("UTF-8", true);
109                 xmlMsgElement.setFeature(
110                         "http://xmlpull.org/v1/doc/features.html#indent-output", true);
111             }
112             xmlMsgElement.startTag(null, "MAP-msg-listing");
113             xmlMsgElement.attribute(null, "version", version);
114             // Do the XML encoding of list
115             for (BluetoothMapMessageListingElement element : mList) {
116                 element.encode(xmlMsgElement, includeThreadId); // Append the list element
117             }
118             xmlMsgElement.endTag(null, "MAP-msg-listing");
119             xmlMsgElement.endDocument();
120         } catch (IllegalArgumentException e) {
121             Log.w(TAG, e);
122         } catch (IllegalStateException e) {
123             Log.w(TAG, e);
124         } catch (IOException e) {
125             Log.w(TAG, e);
126         }
127         /* Fix IOT issue to replace '&amp;' by '&', &lt; by < and '&gt; by '>' in MessageListing */
128         if (DeviceWorkArounds.addressStartsWith(BluetoothMapService.getRemoteDevice().getAddress(),
129                     DeviceWorkArounds.BREZZA_ZDI_CARKIT)) {
130             return sw.toString()
131                     .replaceAll("&amp;", "&")
132                     .replaceAll("&lt;", "<")
133                     .replaceAll("&gt;", ">")
134                     .getBytes("UTF-8");
135         }
136         return sw.toString().getBytes("UTF-8");
137     }
138 
sort()139     public void sort() {
140         Collections.sort(mList);
141     }
142 
segment(int count, int offset)143     public void segment(int count, int offset) {
144         count = Math.min(count, mList.size() - offset);
145         if (count > 0) {
146             mList = mList.subList(offset, offset + count);
147             if (mList == null) {
148                 mList = new ArrayList<BluetoothMapMessageListingElement>(); // Return an empty list
149             }
150         } else {
151             if (offset > mList.size()) {
152                 mList = new ArrayList<BluetoothMapMessageListingElement>();
153                 Log.d(TAG, "offset greater than list size. Returning empty list");
154             } else {
155                 mList = mList.subList(offset, mList.size());
156             }
157         }
158     }
159 }
160