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 MediaModel(Context context, String tag, String contentType, String src, Uri uri)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 MediaModel(Context context, String tag, String contentType, String src, byte[] data)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 MediaModel(Context context, String tag, String contentType, String src, DrmWrapper wrapper)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 getBegin()108 public int getBegin() { 109 return mBegin; 110 } 111 setBegin(int begin)112 public void setBegin(int begin) { 113 mBegin = begin; 114 notifyModelChanged(true); 115 } 116 getDuration()117 public int getDuration() { 118 return mDuration; 119 } 120 setDuration(int duration)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 getTag()137 public String getTag() { 138 return mTag; 139 } 140 getContentType()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 */ getUri()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 */ getUriWithDrmCheck()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 getData()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 */ setUri(Uri uri)188 void setUri(Uri uri) { 189 mUri = uri; 190 } 191 192 /** 193 * @return the mSrc 194 */ getSrc()195 public String getSrc() { 196 return mSrc; 197 } 198 199 /** 200 * @return the mFill 201 */ getFill()202 public short getFill() { 203 return mFill; 204 } 205 206 /** 207 * @param fill the mFill to set 208 */ setFill(short fill)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 */ getMediaResizable()219 public boolean getMediaResizable() { 220 return mMediaResizeable; 221 } 222 223 /** 224 * @return the size of the attached media 225 */ getMediaSize()226 public int getMediaSize() { 227 return mSize; 228 } 229 isText()230 public boolean isText() { 231 return mTag.equals(SmilHelper.ELEMENT_TAG_TEXT); 232 } 233 isImage()234 public boolean isImage() { 235 return mTag.equals(SmilHelper.ELEMENT_TAG_IMAGE); 236 } 237 isVideo()238 public boolean isVideo() { 239 return mTag.equals(SmilHelper.ELEMENT_TAG_VIDEO); 240 } 241 isAudio()242 public boolean isAudio() { 243 return mTag.equals(SmilHelper.ELEMENT_TAG_AUDIO); 244 } 245 isDrmProtected()246 public boolean isDrmProtected() { 247 return mDrmObjectWrapper != null; 248 } 249 isAllowedToForward()250 public boolean isAllowedToForward() { 251 return mDrmObjectWrapper.isAllowedToForward(); 252 } 253 initMediaDuration()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 int duration = 0; 261 try { 262 retriever.setDataSource(mContext, mUri); 263 String dur = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); 264 if (dur != null) { 265 duration = Integer.parseInt(dur); 266 } 267 mDuration = duration; 268 } catch (Exception ex) { 269 Log.e(TAG, "MediaMetadataRetriever failed to get duration for " + mUri.getPath(), ex); 270 throw new MmsException(ex); 271 } finally { 272 retriever.release(); 273 } 274 } 275 initMediaSize()276 private void initMediaSize() throws MmsException { 277 ContentResolver cr = mContext.getContentResolver(); 278 InputStream input = null; 279 try { 280 input = cr.openInputStream(mUri); 281 if (input instanceof FileInputStream) { 282 // avoid reading the whole stream to get its length 283 FileInputStream f = (FileInputStream) input; 284 mSize = (int) f.getChannel().size(); 285 } else { 286 while (-1 != input.read()) { 287 mSize++; 288 } 289 } 290 291 } catch (IOException e) { 292 // Ignore 293 Log.e(TAG, "IOException caught while opening or reading stream", e); 294 if (e instanceof FileNotFoundException) { 295 throw new MmsException(e.getMessage()); 296 } 297 } finally { 298 if (null != input) { 299 try { 300 input.close(); 301 } catch (IOException e) { 302 // Ignore 303 Log.e(TAG, "IOException caught while closing stream", e); 304 } 305 } 306 } 307 } 308 isMmsUri(Uri uri)309 public static boolean isMmsUri(Uri uri) { 310 return uri.getAuthority().startsWith("mms"); 311 } 312 getSeekTo()313 public int getSeekTo() { 314 return mSeekTo; 315 } 316 appendAction(MediaAction action)317 public void appendAction(MediaAction action) { 318 mMediaActions.add(action); 319 } 320 getCurrentAction()321 public MediaAction getCurrentAction() { 322 if (0 == mMediaActions.size()) { 323 return MediaAction.NO_ACTIVE_ACTION; 324 } 325 return mMediaActions.remove(0); 326 } 327 isPlayable()328 protected boolean isPlayable() { 329 return false; 330 } 331 getDrmObject()332 public DrmWrapper getDrmObject() { 333 return mDrmObjectWrapper; 334 } 335 pauseMusicPlayer()336 protected void pauseMusicPlayer() { 337 if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 338 Log.d(TAG, "pauseMusicPlayer"); 339 } 340 341 Intent i = new Intent(MUSIC_SERVICE_ACTION); 342 i.putExtra("command", "pause"); 343 mContext.sendBroadcast(i); 344 } 345 346 /** 347 * If the attached media is resizeable, resize it to fit within the byteLimit. Save the 348 * new part in the pdu. 349 * @param byteLimit the max size of the media attachment 350 * @throws MmsException 351 */ resizeMedia(int byteLimit, long messageId)352 protected void resizeMedia(int byteLimit, long messageId) throws MmsException { 353 } 354 } 355