• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.email;
18 
19 import com.android.emailcommon.internet.MimeUtility;
20 import com.android.emailcommon.provider.EmailContent.Attachment;
21 import com.android.emailcommon.utility.AttachmentUtilities;
22 import com.android.emailcommon.utility.Utility;
23 
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.database.Cursor;
29 import android.net.ConnectivityManager;
30 import android.net.Uri;
31 import android.provider.Settings;
32 import android.text.TextUtils;
33 
34 import java.util.List;
35 
36 /**
37  * Encapsulates commonly used attachment information related to suitability for viewing and saving,
38  * based on the attachment's filename and mime type.
39  */
40 public class AttachmentInfo {
41     // Projection which can be used with the constructor taking a Cursor argument
42     public static final String[] PROJECTION = new String[] {Attachment.RECORD_ID, Attachment.SIZE,
43         Attachment.FILENAME, Attachment.MIME_TYPE, Attachment.ACCOUNT_KEY, Attachment.FLAGS};
44     // Offsets into PROJECTION
45     public static final int COLUMN_ID = 0;
46     public static final int COLUMN_SIZE = 1;
47     public static final int COLUMN_FILENAME = 2;
48     public static final int COLUMN_MIME_TYPE = 3;
49     public static final int COLUMN_ACCOUNT_KEY = 4;
50     public static final int COLUMN_FLAGS = 5;
51 
52     /** Attachment not denied */
53     public static final int ALLOW           = 0x00;
54     /** Attachment suspected of being malware */
55     public static final int DENY_MALWARE    = 0x01;
56     /** Attachment too large; must download over wi-fi */
57     public static final int DENY_WIFIONLY   = 0x02;
58     /** No receiving intent to handle attachment type */
59     public static final int DENY_NOINTENT   = 0x04;
60     /** Side load of applications is disabled */
61     public static final int DENY_NOSIDELOAD = 0x08;
62     // TODO Remove DENY_APKINSTALL when we can install directly from the Email activity
63     /** Unable to install any APK */
64     public static final int DENY_APKINSTALL = 0x10;
65     /** Security policy prohibits install */
66     public static final int DENY_POLICY = 0x20;
67 
68     public final long mId;
69     public final long mSize;
70     public final String mName;
71     public final String mContentType;
72     public final long mAccountKey;
73     public final int mFlags;
74 
75     /** Whether or not this attachment can be viewed */
76     public final boolean mAllowView;
77     /** Whether or not this attachment can be saved */
78     public final boolean mAllowSave;
79     /** Whether or not this attachment can be installed [only true for APKs] */
80     public final boolean mAllowInstall;
81     /** Reason(s) why this attachment is denied from being viewed */
82     public final int mDenyFlags;
83 
AttachmentInfo(Context context, Attachment attachment)84     public AttachmentInfo(Context context, Attachment attachment) {
85         this(context, attachment.mId, attachment.mSize, attachment.mFileName, attachment.mMimeType,
86                 attachment.mAccountKey, attachment.mFlags);
87     }
88 
AttachmentInfo(Context context, Cursor c)89     public AttachmentInfo(Context context, Cursor c) {
90         this(context, c.getLong(COLUMN_ID), c.getLong(COLUMN_SIZE), c.getString(COLUMN_FILENAME),
91                 c.getString(COLUMN_MIME_TYPE), c.getLong(COLUMN_ACCOUNT_KEY),
92                 c.getInt(COLUMN_FLAGS));
93     }
94 
AttachmentInfo(Context context, AttachmentInfo info)95     public AttachmentInfo(Context context, AttachmentInfo info) {
96         this(context, info.mId, info.mSize, info.mName, info.mContentType, info.mAccountKey,
97                 info.mFlags);
98     }
99 
AttachmentInfo(Context context, long id, long size, String fileName, String mimeType, long accountKey, int flags)100     public AttachmentInfo(Context context, long id, long size, String fileName, String mimeType,
101             long accountKey, int flags) {
102         mSize = size;
103         mContentType = AttachmentUtilities.inferMimeType(fileName, mimeType);
104         mName = fileName;
105         mId = id;
106         mAccountKey = accountKey;
107         mFlags = flags;
108         boolean canView = true;
109         boolean canSave = true;
110         boolean canInstall = false;
111         int denyFlags = ALLOW;
112 
113         // Don't enable the "save" button if we've got no place to save the file
114         if (!Utility.isExternalStorageMounted()) {
115             canSave = false;
116         }
117 
118         // Check for acceptable / unacceptable attachments by MIME-type
119         if ((!MimeUtility.mimeTypeMatches(mContentType,
120                 AttachmentUtilities.ACCEPTABLE_ATTACHMENT_VIEW_TYPES)) ||
121             (MimeUtility.mimeTypeMatches(mContentType,
122                     AttachmentUtilities.UNACCEPTABLE_ATTACHMENT_VIEW_TYPES))) {
123             canView = false;
124         }
125 
126         // Check for unacceptable attachments by filename extension
127         String extension = AttachmentUtilities.getFilenameExtension(mName);
128         if (!TextUtils.isEmpty(extension) &&
129                 Utility.arrayContains(AttachmentUtilities.UNACCEPTABLE_ATTACHMENT_EXTENSIONS,
130                         extension)) {
131             canView = false;
132             canSave = false;
133             denyFlags |= DENY_MALWARE;
134         }
135 
136         // Check for policy restrictions on download
137         if ((flags & Attachment.FLAG_POLICY_DISALLOWS_DOWNLOAD) != 0) {
138             canView = false;
139             canSave = false;
140             denyFlags |= DENY_POLICY;
141         }
142 
143         // Check for installable attachments by filename extension
144         extension = AttachmentUtilities.getFilenameExtension(mName);
145         if (!TextUtils.isEmpty(extension) &&
146                 Utility.arrayContains(AttachmentUtilities.INSTALLABLE_ATTACHMENT_EXTENSIONS,
147                         extension)) {
148             boolean sideloadEnabled;
149             sideloadEnabled = Settings.Secure.getInt(context.getContentResolver(),
150                     Settings.Secure.INSTALL_NON_MARKET_APPS, 0 /* sideload disabled */) != 0;
151             canSave &= sideloadEnabled;
152             canView = canSave;
153             canInstall = canSave;
154             if (!sideloadEnabled) {
155                 denyFlags |= DENY_NOSIDELOAD;
156             }
157         }
158 
159         // Check for file size exceeded
160         // The size limit is overridden when on a wifi connection - any size is OK
161         if (mSize > AttachmentUtilities.MAX_ATTACHMENT_DOWNLOAD_SIZE) {
162             int networkType = EmailConnectivityManager.getActiveNetworkType(context);
163             if (networkType != ConnectivityManager.TYPE_WIFI) {
164                 canView = false;
165                 canSave = false;
166                 denyFlags |= DENY_WIFIONLY;
167             }
168         }
169 
170         // Check to see if any activities can view this attachment; if none, we can't view it
171         Intent intent = getAttachmentIntent(context, 0);
172         PackageManager pm = context.getPackageManager();
173         List<ResolveInfo> activityList = pm.queryIntentActivities(intent, 0 /*no account*/);
174         if (activityList.isEmpty()) {
175             canView = false;
176             canSave = false;
177             denyFlags |= DENY_NOINTENT;
178         }
179 
180         mAllowView = canView;
181         mAllowSave = canSave;
182         mAllowInstall = canInstall;
183         mDenyFlags = denyFlags;
184     }
185 
186     /**
187      * Returns an <code>Intent</code> to load the given attachment.
188      * @param context the caller's context
189      * @param accountId the account associated with the attachment (or 0 if we don't need to
190      *     resolve from attachmentUri to contentUri)
191      * @return an Intent suitable for viewing the attachment
192      */
getAttachmentIntent(Context context, long accountId)193     public Intent getAttachmentIntent(Context context, long accountId) {
194         Uri contentUri = getUriForIntent(context, accountId);
195         Intent intent = new Intent(Intent.ACTION_VIEW);
196         intent.setDataAndType(contentUri, mContentType);
197         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
198                 | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
199         return intent;
200     }
201 
getUriForIntent(Context context, long accountId)202     protected Uri getUriForIntent(Context context, long accountId) {
203         Uri contentUri = AttachmentUtilities.getAttachmentUri(accountId, mId);
204         if (accountId > 0) {
205             contentUri = AttachmentUtilities.resolveAttachmentIdToContentUri(
206                     context.getContentResolver(), contentUri);
207         }
208 
209         return contentUri;
210     }
211 
212     /**
213      * An attachment is eligible for download if it can either be viewed or saved (or both)
214      * @return whether the attachment is eligible for download
215      */
isEligibleForDownload()216     public boolean isEligibleForDownload() {
217         return mAllowView || mAllowSave;
218     }
219 
220     @Override
hashCode()221     public int hashCode() {
222         return (int) (mId ^ (mId >>> 32));
223     }
224 
225     @Override
equals(Object o)226     public boolean equals(Object o) {
227         if (o == this) {
228             return true;
229         }
230 
231         if ((o == null) || (o.getClass() != getClass())) {
232             return false;
233         }
234 
235         return ((AttachmentInfo) o).mId == mId;
236     }
237 
238     @Override
toString()239     public String toString() {
240         return "{Attachment " + mId + ":" + mName + "," + mContentType + "," + mSize + "}";
241     }
242 }
243