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         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 
    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                 // sometimes mSize will be zero here. It's tempting to count the bytes as the code
    286                 // does below, but that turns out to be very slow. We'll deal with a zero size
    287                 // when we resize the media.
    288             } else {
    289                 while (-1 != input.read()) {
    290                     mSize++;
    291                 }
    292             }
    293 
    294         } catch (IOException e) {
    295             // Ignore
    296             Log.e(TAG, "IOException caught while opening or reading stream", e);
    297             if (e instanceof FileNotFoundException) {
    298                 throw new MmsException(e.getMessage());
    299             }
    300         } finally {
    301             if (null != input) {
    302                 try {
    303                     input.close();
    304                 } catch (IOException e) {
    305                     // Ignore
    306                     Log.e(TAG, "IOException caught while closing stream", e);
    307                 }
    308             }
    309         }
    310     }
    311 
    312     public static boolean isMmsUri(Uri uri) {
    313         return uri.getAuthority().startsWith("mms");
    314     }
    315 
    316     public int getSeekTo() {
    317         return mSeekTo;
    318     }
    319 
    320     public void appendAction(MediaAction action) {
    321         mMediaActions.add(action);
    322     }
    323 
    324     public MediaAction getCurrentAction() {
    325         if (0 == mMediaActions.size()) {
    326             return MediaAction.NO_ACTIVE_ACTION;
    327         }
    328         return mMediaActions.remove(0);
    329     }
    330 
    331     protected boolean isPlayable() {
    332         return false;
    333     }
    334 
    335     public DrmWrapper getDrmObject() {
    336         return mDrmObjectWrapper;
    337     }
    338 
    339     protected void pauseMusicPlayer() {
    340         if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
    341             Log.d(TAG, "pauseMusicPlayer");
    342         }
    343 
    344         Intent i = new Intent(MUSIC_SERVICE_ACTION);
    345         i.putExtra("command", "pause");
    346         mContext.sendBroadcast(i);
    347     }
    348 
    349     /**
    350      * If the attached media is resizeable, resize it to fit within the byteLimit. Save the
    351      * new part in the pdu.
    352      * @param byteLimit the max size of the media attachment
    353      * @throws MmsException
    354      */
    355     protected void resizeMedia(int byteLimit, long messageId) throws MmsException {
    356     }
    357 }
    358