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