Home | History | Annotate | Download | only in model
      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