MediaModel.java revision e8e0d2714a53fa942ef95f5e7b89f2d3cc3a69b8
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
18package com.android.mms.model;
19
20import com.android.mms.R;
21import com.android.mms.LogTag;
22import android.drm.mobile1.DrmException;
23import com.android.mms.drm.DrmWrapper;
24import android.media.MediaMetadataRetriever;        // TODO: remove dependency for SDK build
25import com.google.android.mms.MmsException;
26import com.android.mms.drm.DrmUtils;
27
28import org.w3c.dom.events.EventListener;
29
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.net.Uri;
34import android.util.Log;
35
36import java.io.FileInputStream;
37import java.io.FileNotFoundException;
38import java.io.IOException;
39import java.io.InputStream;
40import java.util.ArrayList;
41
42public 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            } 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
309    public static boolean isMmsUri(Uri uri) {
310        return uri.getAuthority().startsWith("mms");
311    }
312
313    public int getSeekTo() {
314        return mSeekTo;
315    }
316
317    public void appendAction(MediaAction action) {
318        mMediaActions.add(action);
319    }
320
321    public MediaAction getCurrentAction() {
322        if (0 == mMediaActions.size()) {
323            return MediaAction.NO_ACTIVE_ACTION;
324        }
325        return mMediaActions.remove(0);
326    }
327
328    protected boolean isPlayable() {
329        return false;
330    }
331
332    public DrmWrapper getDrmObject() {
333        return mDrmObjectWrapper;
334    }
335
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     */
352    protected void resizeMedia(int byteLimit, long messageId) throws MmsException {
353    }
354}
355