1 /* 2 * Copyright (C) 2014 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 android.bluetooth.client.map; 18 import android.util.Log; 19 20 import org.json.JSONException; 21 import org.json.JSONObject; 22 import org.xmlpull.v1.XmlPullParser; 23 import org.xmlpull.v1.XmlPullParserException; 24 import org.xmlpull.v1.XmlPullParserFactory; 25 26 import java.io.DataInputStream; 27 import java.io.IOException; 28 import java.math.BigInteger; 29 import java.util.HashMap; 30 31 /** 32 * Object representation of event report received by MNS 33 * <p> 34 * This object will be received in {@link BluetoothMasClient#EVENT_EVENT_REPORT} 35 * callback message. 36 */ 37 public class BluetoothMapEventReport { 38 39 private final static String TAG = "BluetoothMapEventReport"; 40 41 public enum Type { 42 NEW_MESSAGE("NewMessage"), DELIVERY_SUCCESS("DeliverySuccess"), 43 SENDING_SUCCESS("SendingSuccess"), DELIVERY_FAILURE("DeliveryFailure"), 44 SENDING_FAILURE("SendingFailure"), MEMORY_FULL("MemoryFull"), 45 MEMORY_AVAILABLE("MemoryAvailable"), MESSAGE_DELETED("MessageDeleted"), 46 MESSAGE_SHIFT("MessageShift"); 47 48 private final String mSpecName; 49 50 private Type(String specName) { 51 mSpecName = specName; 52 } 53 54 @Override 55 public String toString() { 56 return mSpecName; 57 } 58 } 59 60 private final Type mType; 61 62 private final String mHandle; 63 64 private final String mFolder; 65 66 private final String mOldFolder; 67 68 private final BluetoothMapBmessage.Type mMsgType; 69 70 private BluetoothMapEventReport(HashMap<String, String> attrs) throws IllegalArgumentException { 71 mType = parseType(attrs.get("type")); 72 73 if (mType != Type.MEMORY_FULL && mType != Type.MEMORY_AVAILABLE) { 74 String handle = attrs.get("handle"); 75 try { 76 /* just to validate */ 77 new BigInteger(attrs.get("handle"), 16); 78 79 mHandle = attrs.get("handle"); 80 } catch (NumberFormatException e) { 81 throw new IllegalArgumentException("Invalid value for handle:" + handle); 82 } 83 } else { 84 mHandle = null; 85 } 86 87 mFolder = attrs.get("folder"); 88 89 mOldFolder = attrs.get("old_folder"); 90 91 if (mType != Type.MEMORY_FULL && mType != Type.MEMORY_AVAILABLE) { 92 String s = attrs.get("msg_type"); 93 94 if ("".equals(s)) { 95 // Some phones (e.g. SGS3 for MessageDeleted) send empty 96 // msg_type, in such case leave it as null rather than throw 97 // parse exception 98 mMsgType = null; 99 } else { 100 mMsgType = parseMsgType(s); 101 } 102 } else { 103 mMsgType = null; 104 } 105 } 106 107 private Type parseType(String type) throws IllegalArgumentException { 108 for (Type t : Type.values()) { 109 if (t.toString().equals(type)) { 110 return t; 111 } 112 } 113 114 throw new IllegalArgumentException("Invalid value for type: " + type); 115 } 116 117 private BluetoothMapBmessage.Type parseMsgType(String msgType) throws IllegalArgumentException { 118 for (BluetoothMapBmessage.Type t : BluetoothMapBmessage.Type.values()) { 119 if (t.name().equals(msgType)) { 120 return t; 121 } 122 } 123 124 throw new IllegalArgumentException("Invalid value for msg_type: " + msgType); 125 } 126 127 /** 128 * @return {@link BluetoothMapEventReport.Type} object corresponding to 129 * <code>type</code> application parameter in MAP specification 130 */ 131 public Type getType() { 132 return mType; 133 } 134 135 /** 136 * @return value corresponding to <code>handle</code> parameter in MAP 137 * specification 138 */ 139 public String getHandle() { 140 return mHandle; 141 } 142 143 /** 144 * @return value corresponding to <code>folder</code> parameter in MAP 145 * specification 146 */ 147 public String getFolder() { 148 return mFolder; 149 } 150 151 /** 152 * @return value corresponding to <code>old_folder</code> parameter in MAP 153 * specification 154 */ 155 public String getOldFolder() { 156 return mOldFolder; 157 } 158 159 /** 160 * @return {@link BluetoothMapBmessage.Type} object corresponding to 161 * <code>msg_type</code> application parameter in MAP specification 162 */ 163 public BluetoothMapBmessage.Type getMsgType() { 164 return mMsgType; 165 } 166 167 @Override 168 public String toString() { 169 JSONObject json = new JSONObject(); 170 171 try { 172 json.put("type", mType); 173 json.put("handle", mHandle); 174 json.put("folder", mFolder); 175 json.put("old_folder", mOldFolder); 176 json.put("msg_type", mMsgType); 177 } catch (JSONException e) { 178 // do nothing 179 } 180 181 return json.toString(); 182 } 183 184 static BluetoothMapEventReport fromStream(DataInputStream in) { 185 BluetoothMapEventReport ev = null; 186 187 try { 188 XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser(); 189 xpp.setInput(in, "utf-8"); 190 191 int event = xpp.getEventType(); 192 while (event != XmlPullParser.END_DOCUMENT) { 193 switch (event) { 194 case XmlPullParser.START_TAG: 195 if (xpp.getName().equals("event")) { 196 HashMap<String, String> attrs = new HashMap<String, String>(); 197 198 for (int i = 0; i < xpp.getAttributeCount(); i++) { 199 attrs.put(xpp.getAttributeName(i), xpp.getAttributeValue(i)); 200 } 201 202 ev = new BluetoothMapEventReport(attrs); 203 204 // return immediately, only one event should be here 205 return ev; 206 } 207 break; 208 } 209 210 event = xpp.next(); 211 } 212 213 } catch (XmlPullParserException e) { 214 Log.e(TAG, "XML parser error when parsing XML", e); 215 } catch (IOException e) { 216 Log.e(TAG, "I/O error when parsing XML", e); 217 } catch (IllegalArgumentException e) { 218 Log.e(TAG, "Invalid event received", e); 219 } 220 221 return ev; 222 } 223 } 224