1 /* 2 * Copyright (C) 2008 Esmertec AG. 3 * Copyright (C) 2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.mms.model; 19 20 import com.android.mms.R; 21 import com.android.mms.LogTag; 22 import android.drm.mobile1.DrmException; 23 import com.android.mms.drm.DrmWrapper; 24 import android.media.MediaMetadataRetriever; // TODO: remove dependency for SDK build 25 import com.google.android.mms.MmsException; 26 import com.android.mms.drm.DrmUtils; 27 28 import org.w3c.dom.events.EventListener; 29 30 import android.content.ContentResolver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.net.Uri; 34 import android.util.Log; 35 36 import java.io.FileInputStream; 37 import java.io.FileNotFoundException; 38 import java.io.IOException; 39 import java.io.InputStream; 40 import java.util.ArrayList; 41 42 public abstract class MediaModel extends Model implements EventListener { 43 protected static final String TAG = "Mms/media"; 44 45 private final static String MUSIC_SERVICE_ACTION = "com.android.music.musicservicecommand"; 46 47 protected Context mContext; 48 protected int mBegin; 49 protected int mDuration; 50 protected String mTag; 51 protected String mSrc; 52 protected String mContentType; 53 private Uri mUri; 54 private byte[] mData; 55 protected short mFill; 56 protected int mSize; 57 protected int mSeekTo; 58 protected DrmWrapper mDrmObjectWrapper; 59 protected boolean mMediaResizeable; 60 61 private final ArrayList<MediaAction> mMediaActions; 62 public static enum MediaAction { 63 NO_ACTIVE_ACTION, 64 START, 65 STOP, 66 PAUSE, 67 SEEK, 68 } 69 70 public MediaModel(Context context, String tag, String contentType, 71 String src, Uri uri) throws MmsException { 72 mContext = context; 73 mTag = tag; 74 mContentType = contentType; 75 mSrc = src; 76 mUri = uri; 77 initMediaSize(); 78 mMediaActions = new ArrayList<MediaAction>(); 79 } 80 81 public MediaModel(Context context, String tag, String contentType, 82 String src, byte[] data) { 83 if (data == null) { 84 throw new IllegalArgumentException("data may not be null."); 85 } 86 87 mContext = context; 88 mTag = tag; 89 mContentType = contentType; 90 mSrc = src; 91 mData = data; 92 mSize = data.length; 93 mMediaActions = new ArrayList<MediaAction>(); 94 } 95 96 public MediaModel(Context context, String tag, String contentType, 97 String src, DrmWrapper wrapper) throws IOException { 98 mContext = context; 99 mTag = tag; 100 mContentType = contentType; 101 mSrc = src; 102 mDrmObjectWrapper = wrapper; 103 mUri = DrmUtils.insert(context, wrapper); 104 mSize = wrapper.getOriginalData().length; 105 mMediaActions = new ArrayList<MediaAction>(); 106 } 107 108 public int getBegin() { 109 return mBegin; 110 } 111 112 public void setBegin(int begin) { 113 mBegin = begin; 114 notifyModelChanged(true); 115 } 116 117 public int getDuration() { 118 return mDuration; 119 } 120 121 public void setDuration(int duration) { 122 if (isPlayable() && (duration < 0)) { 123 // 'indefinite' duration, we should try to find its exact value; 124 try { 125 initMediaDuration(); 126 } catch (MmsException e) { 127 // On error, keep default duration. 128 Log.e(TAG, e.getMessage(), e); 129 return; 130 } 131 } else { 132 mDuration = duration; 133 } 134 notifyModelChanged(true); 135 } 136 137 public String getTag() { 138 return mTag; 139 } 140 141 public String getContentType() { 142 return mContentType; 143 } 144 145 /** 146 * Get the URI of the media without checking DRM rights. Use this method 147 * only if the media is NOT DRM protected. 148 * 149 * @return The URI of the media. 150 */ 151 public Uri getUri() { 152 return mUri; 153 } 154 155 /** 156 * Get the URI of the media with checking DRM rights. Use this method 157 * if the media is probably DRM protected. 158 * 159 * @return The URI of the media. 160 * @throws DrmException Insufficient DRM rights detected. 161 */ 162 public Uri getUriWithDrmCheck() throws DrmException { 163 if (mUri != null) { 164 if (isDrmProtected() && !mDrmObjectWrapper.consumeRights()) { 165 throw new DrmException("Insufficient DRM rights."); 166 } 167 } 168 return mUri; 169 } 170 171 public byte[] getData() throws DrmException { 172 if (mData != null) { 173 if (isDrmProtected() && !mDrmObjectWrapper.consumeRights()) { 174 throw new DrmException( 175 mContext.getString(R.string.insufficient_drm_rights)); 176 } 177 178 byte[] data = new byte[mData.length]; 179 System.arraycopy(mData, 0, data, 0, mData.length); 180 return data; 181 } 182 return null; 183 } 184 185 /** 186 * @param uri the mUri to set 187 */ 188 void setUri(Uri uri) { 189 mUri = uri; 190 } 191 192 /** 193 * @return the mSrc 194 */ 195 public String getSrc() { 196 return mSrc; 197 } 198 199 /** 200 * @return the mFill 201 */ 202 public short getFill() { 203 return mFill; 204 } 205 206 /** 207 * @param fill the mFill to set 208 */ 209 public void setFill(short fill) { 210 mFill = fill; 211 notifyModelChanged(true); 212 } 213 214 /** 215 * @return whether the media is resizable or not. For instance, a picture can be resized 216 * to smaller dimensions or lower resolution. Other media, such as video and sounds, aren't 217 * currently able to be resized. 218 */ 219 public boolean getMediaResizable() { 220 return mMediaResizeable; 221 } 222 223 /** 224 * @return the size of the attached media 225 */ 226 public int getMediaSize() { 227 return mSize; 228 } 229 230 public boolean isText() { 231 return mTag.equals(SmilHelper.ELEMENT_TAG_TEXT); 232 } 233 234 public boolean isImage() { 235 return mTag.equals(SmilHelper.ELEMENT_TAG_IMAGE); 236 } 237 238 public boolean isVideo() { 239 return mTag.equals(SmilHelper.ELEMENT_TAG_VIDEO); 240 } 241 242 public boolean isAudio() { 243 return mTag.equals(SmilHelper.ELEMENT_TAG_AUDIO); 244 } 245 246 public boolean isDrmProtected() { 247 return mDrmObjectWrapper != null; 248 } 249 250 public boolean isAllowedToForward() { 251 return mDrmObjectWrapper.isAllowedToForward(); 252 } 253 254 protected void initMediaDuration() throws MmsException { 255 if (mUri == null) { 256 throw new IllegalArgumentException("Uri may not be null."); 257 } 258 259 MediaMetadataRetriever retriever = new MediaMetadataRetriever(); 260 retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY); 261 int duration = 0; 262 try { 263 retriever.setDataSource(mContext, mUri); 264 String dur = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); 265 if (dur != null) { 266 duration = Integer.parseInt(dur); 267 } 268 mDuration = duration; 269 } catch (Exception ex) { 270 Log.e(TAG, "MediaMetadataRetriever failed to get duration for " + mUri.getPath(), ex); 271 throw new MmsException(ex); 272 } finally { 273 retriever.release(); 274 } 275 } 276 277 private void initMediaSize() throws MmsException { 278 ContentResolver cr = mContext.getContentResolver(); 279 InputStream input = null; 280 try { 281 input = cr.openInputStream(mUri); 282 if (input instanceof FileInputStream) { 283 // avoid reading the whole stream to get its length 284 FileInputStream f = (FileInputStream) input; 285 mSize = (int) f.getChannel().size(); 286 } else { 287 while (-1 != input.read()) { 288 mSize++; 289 } 290 } 291 292 } catch (IOException e) { 293 // Ignore 294 Log.e(TAG, "IOException caught while opening or reading stream", e); 295 if (e instanceof FileNotFoundException) { 296 throw new MmsException(e.getMessage()); 297 } 298 } finally { 299 if (null != input) { 300 try { 301 input.close(); 302 } catch (IOException e) { 303 // Ignore 304 Log.e(TAG, "IOException caught while closing stream", e); 305 } 306 } 307 } 308 } 309 310 public static boolean isMmsUri(Uri uri) { 311 return uri.getAuthority().startsWith("mms"); 312 } 313 314 public int getSeekTo() { 315 return mSeekTo; 316 } 317 318 public void appendAction(MediaAction action) { 319 mMediaActions.add(action); 320 } 321 322 public MediaAction getCurrentAction() { 323 if (0 == mMediaActions.size()) { 324 return MediaAction.NO_ACTIVE_ACTION; 325 } 326 return mMediaActions.remove(0); 327 } 328 329 protected boolean isPlayable() { 330 return false; 331 } 332 333 public DrmWrapper getDrmObject() { 334 return mDrmObjectWrapper; 335 } 336 337 protected void pauseMusicPlayer() { 338 if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 339 Log.d(TAG, "pauseMusicPlayer"); 340 } 341 342 Intent i = new Intent(MUSIC_SERVICE_ACTION); 343 i.putExtra("command", "pause"); 344 mContext.sendBroadcast(i); 345 } 346 347 /** 348 * If the attached media is resizeable, resize it to fit within the byteLimit. Save the 349 * new part in the pdu. 350 * @param byteLimit the max size of the media attachment 351 * @throws MmsException 352 */ 353 protected void resizeMedia(int byteLimit, long messageId) throws MmsException { 354 } 355 } 356