1/*
2 * Copyright (C) 2012 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
17package com.android.providers.downloads;
18
19import static android.app.DownloadManager.COLUMN_LOCAL_FILENAME;
20import static android.app.DownloadManager.COLUMN_MEDIA_TYPE;
21import static android.app.DownloadManager.COLUMN_URI;
22import static android.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI;
23
24import static com.android.providers.downloads.Constants.TAG;
25
26import android.app.DownloadManager;
27import android.content.ActivityNotFoundException;
28import android.content.ContentUris;
29import android.content.Context;
30import android.content.Intent;
31import android.content.pm.PackageInstaller;
32import android.database.Cursor;
33import android.net.Uri;
34import android.os.Process;
35import android.provider.DocumentsContract;
36import android.provider.Downloads.Impl.RequestHeaders;
37import android.util.Log;
38
39import java.io.File;
40
41public class OpenHelper {
42    /**
43     * Build and start an {@link Intent} to view the download with given ID,
44     * handling subtleties around installing packages.
45     */
46    public static boolean startViewIntent(Context context, long id, int intentFlags) {
47        final Intent intent = OpenHelper.buildViewIntent(context, id);
48        if (intent == null) {
49            Log.w(TAG, "No intent built for " + id);
50            return false;
51        }
52
53        intent.addFlags(intentFlags);
54        try {
55            context.startActivity(intent);
56            return true;
57        } catch (ActivityNotFoundException e) {
58            Log.w(TAG, "Failed to start " + intent + ": " + e);
59            return false;
60        }
61    }
62
63    /**
64     * Build an {@link Intent} to view the download with given ID, handling
65     * subtleties around installing packages.
66     */
67    private static Intent buildViewIntent(Context context, long id) {
68        final DownloadManager downManager = (DownloadManager) context.getSystemService(
69                Context.DOWNLOAD_SERVICE);
70        downManager.setAccessAllDownloads(true);
71        downManager.setAccessFilename(true);
72
73        final Cursor cursor = downManager.query(new DownloadManager.Query().setFilterById(id));
74        try {
75            if (!cursor.moveToFirst()) {
76                return null;
77            }
78
79            final File file = getCursorFile(cursor, COLUMN_LOCAL_FILENAME);
80            String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE);
81            mimeType = DownloadDrmHelper.getOriginalMimeType(context, file, mimeType);
82
83            final Uri documentUri = DocumentsContract.buildDocumentUri(
84                    Constants.STORAGE_AUTHORITY, String.valueOf(id));
85
86            final Intent intent = new Intent(Intent.ACTION_VIEW);
87            intent.setDataAndType(documentUri, mimeType);
88            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
89                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
90
91            if ("application/vnd.android.package-archive".equals(mimeType)) {
92                // Also splice in details about where it came from
93                final Uri remoteUri = getCursorUri(cursor, COLUMN_URI);
94                intent.putExtra(Intent.EXTRA_ORIGINATING_URI, remoteUri);
95                intent.putExtra(Intent.EXTRA_REFERRER, getRefererUri(context, id));
96                intent.putExtra(Intent.EXTRA_ORIGINATING_UID, getOriginatingUid(context, id));
97            }
98
99            return intent;
100        } finally {
101            cursor.close();
102        }
103    }
104
105    private static Uri getRefererUri(Context context, long id) {
106        final Uri headersUri = Uri.withAppendedPath(
107                ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id),
108                RequestHeaders.URI_SEGMENT);
109        final Cursor headers = context.getContentResolver()
110                .query(headersUri, null, null, null, null);
111        try {
112            while (headers.moveToNext()) {
113                final String header = getCursorString(headers, RequestHeaders.COLUMN_HEADER);
114                if ("Referer".equalsIgnoreCase(header)) {
115                    return getCursorUri(headers, RequestHeaders.COLUMN_VALUE);
116                }
117            }
118        } finally {
119            headers.close();
120        }
121        return null;
122    }
123
124    private static int getOriginatingUid(Context context, long id) {
125        final Uri uri = ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id);
126        final Cursor cursor = context.getContentResolver().query(uri, new String[]{Constants.UID},
127                null, null, null);
128        if (cursor != null) {
129            try {
130                if (cursor.moveToFirst()) {
131                    final int uid = cursor.getInt(cursor.getColumnIndexOrThrow(Constants.UID));
132                    if (uid != Process.myUid()) {
133                        return uid;
134                    }
135                }
136            } finally {
137                cursor.close();
138            }
139        }
140        return PackageInstaller.SessionParams.UID_UNKNOWN;
141    }
142
143    private static String getCursorString(Cursor cursor, String column) {
144        return cursor.getString(cursor.getColumnIndexOrThrow(column));
145    }
146
147    private static Uri getCursorUri(Cursor cursor, String column) {
148        return Uri.parse(getCursorString(cursor, column));
149    }
150
151    private static File getCursorFile(Cursor cursor, String column) {
152        return new File(cursor.getString(cursor.getColumnIndexOrThrow(column)));
153    }
154}
155