1c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey/*
2c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * Copyright (C) 2012 The Android Open Source Project
3c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey *
4c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * you may not use this file except in compliance with the License.
6c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * You may obtain a copy of the License at
7c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey *
8c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey *
10c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
11c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * See the License for the specific language governing permissions and
14c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey * limitations under the License.
15c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey */
16c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
17c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeypackage com.android.providers.downloads;
18c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
19169ab531ca6bb2f43ed18fe2b03f66c808f12b81Jeff Sharkeyimport static android.app.DownloadManager.COLUMN_LOCAL_FILENAME;
20c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport static android.app.DownloadManager.COLUMN_MEDIA_TYPE;
21c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport static android.app.DownloadManager.COLUMN_URI;
22c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport static android.provider.Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI;
233b5c235df372193d6555a71c096cccf459b22b7fJeff Sharkey
248ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkeyimport static com.android.providers.downloads.Constants.TAG;
25c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
26c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.app.DownloadManager;
278ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkeyimport android.content.ActivityNotFoundException;
28c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.content.ContentUris;
29c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.content.Context;
30c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.content.Intent;
310a1a4287841fa10274c610de31e08765ffa0c269Ben Linimport android.content.pm.PackageInstaller;
32c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.database.Cursor;
33c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.net.Uri;
34b7aea795beec6721c60b59c9d945e4d964466323Suprabh Shuklaimport android.os.Process;
353b4301a153221814ded7e76208e7b93b8f19e9b2Tomasz Mikolajewskiimport android.provider.DocumentsContract;
36c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeyimport android.provider.Downloads.Impl.RequestHeaders;
378ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkeyimport android.util.Log;
38c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
395cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkeyimport java.io.File;
405cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey
41c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkeypublic class OpenHelper {
42c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey    /**
438ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey     * Build and start an {@link Intent} to view the download with given ID,
448ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey     * handling subtleties around installing packages.
45c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey     */
468ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey    public static boolean startViewIntent(Context context, long id, int intentFlags) {
478ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey        final Intent intent = OpenHelper.buildViewIntent(context, id);
488ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey        if (intent == null) {
498ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey            Log.w(TAG, "No intent built for " + id);
508ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey            return false;
518ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey        }
528ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey
538ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey        intent.addFlags(intentFlags);
548ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey        try {
558ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey            context.startActivity(intent);
568ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey            return true;
578ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey        } catch (ActivityNotFoundException e) {
588ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey            Log.w(TAG, "Failed to start " + intent + ": " + e);
598ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey            return false;
608ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey        }
618ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey    }
628ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey
638ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey    /**
648ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey     * Build an {@link Intent} to view the download with given ID, handling
658ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey     * subtleties around installing packages.
668ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey     */
678ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey    private static Intent buildViewIntent(Context context, long id) {
68c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        final DownloadManager downManager = (DownloadManager) context.getSystemService(
69c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                Context.DOWNLOAD_SERVICE);
70c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        downManager.setAccessAllDownloads(true);
71307b464de52444531365a55eb700da6ba2b28ad2Jeff Sharkey        downManager.setAccessFilename(true);
72c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
73c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        final Cursor cursor = downManager.query(new DownloadManager.Query().setFilterById(id));
74c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        try {
75c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey            if (!cursor.moveToFirst()) {
768ec87ea5a5c191554d6205d4c779768664b1724bJeff Sharkey                return null;
77c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey            }
78c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
795cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey            final File file = getCursorFile(cursor, COLUMN_LOCAL_FILENAME);
80169ab531ca6bb2f43ed18fe2b03f66c808f12b81Jeff Sharkey            String mimeType = getCursorString(cursor, COLUMN_MEDIA_TYPE);
815cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey            mimeType = DownloadDrmHelper.getOriginalMimeType(context, file, mimeType);
82c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
833b4301a153221814ded7e76208e7b93b8f19e9b2Tomasz Mikolajewski            final Uri documentUri = DocumentsContract.buildDocumentUri(
843b4301a153221814ded7e76208e7b93b8f19e9b2Tomasz Mikolajewski                    Constants.STORAGE_AUTHORITY, String.valueOf(id));
853b4301a153221814ded7e76208e7b93b8f19e9b2Tomasz Mikolajewski
86c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey            final Intent intent = new Intent(Intent.ACTION_VIEW);
873b5c235df372193d6555a71c096cccf459b22b7fJeff Sharkey            intent.setDataAndType(documentUri, mimeType);
883b5c235df372193d6555a71c096cccf459b22b7fJeff Sharkey            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
893b5c235df372193d6555a71c096cccf459b22b7fJeff Sharkey                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
90c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
91c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey            if ("application/vnd.android.package-archive".equals(mimeType)) {
92c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                // Also splice in details about where it came from
93c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                final Uri remoteUri = getCursorUri(cursor, COLUMN_URI);
94c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                intent.putExtra(Intent.EXTRA_ORIGINATING_URI, remoteUri);
95c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                intent.putExtra(Intent.EXTRA_REFERRER, getRefererUri(context, id));
9646bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver                intent.putExtra(Intent.EXTRA_ORIGINATING_UID, getOriginatingUid(context, id));
97c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey            }
98c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
99c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey            return intent;
100c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        } finally {
101c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey            cursor.close();
102c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        }
103c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey    }
104c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
105c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey    private static Uri getRefererUri(Context context, long id) {
106c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        final Uri headersUri = Uri.withAppendedPath(
107c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id),
108c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                RequestHeaders.URI_SEGMENT);
109c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        final Cursor headers = context.getContentResolver()
110c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                .query(headersUri, null, null, null, null);
111c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        try {
112c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey            while (headers.moveToNext()) {
113c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                final String header = getCursorString(headers, RequestHeaders.COLUMN_HEADER);
114c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                if ("Referer".equalsIgnoreCase(header)) {
115c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                    return getCursorUri(headers, RequestHeaders.COLUMN_VALUE);
116c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey                }
117c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey            }
118c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        } finally {
119c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey            headers.close();
120c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        }
121c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        return null;
122c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey    }
123c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
12446bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver    private static int getOriginatingUid(Context context, long id) {
12546bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver        final Uri uri = ContentUris.withAppendedId(ALL_DOWNLOADS_CONTENT_URI, id);
12646bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver        final Cursor cursor = context.getContentResolver().query(uri, new String[]{Constants.UID},
12746bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver                null, null, null);
12846bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver        if (cursor != null) {
12946bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver            try {
13046bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver                if (cursor.moveToFirst()) {
131b7aea795beec6721c60b59c9d945e4d964466323Suprabh Shukla                    final int uid = cursor.getInt(cursor.getColumnIndexOrThrow(Constants.UID));
132b7aea795beec6721c60b59c9d945e4d964466323Suprabh Shukla                    if (uid != Process.myUid()) {
133b7aea795beec6721c60b59c9d945e4d964466323Suprabh Shukla                        return uid;
134b7aea795beec6721c60b59c9d945e4d964466323Suprabh Shukla                    }
13546bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver                }
13646bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver            } finally {
13746bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver                cursor.close();
13846bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver            }
13946bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver        }
1400a1a4287841fa10274c610de31e08765ffa0c269Ben Lin        return PackageInstaller.SessionParams.UID_UNKNOWN;
14146bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver    }
14246bb7dd18c9b092d352d76ab44a4e52951941b2cBen Gruver
143c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey    private static String getCursorString(Cursor cursor, String column) {
144c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        return cursor.getString(cursor.getColumnIndexOrThrow(column));
145c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey    }
146c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
147c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey    private static Uri getCursorUri(Cursor cursor, String column) {
148c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey        return Uri.parse(getCursorString(cursor, column));
149c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey    }
150c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey
1515cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey    private static File getCursorFile(Cursor cursor, String column) {
1525cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey        return new File(cursor.getString(cursor.getColumnIndexOrThrow(column)));
1535cff4ecb10e89e4fb39cd9e39b8753a31efbe3ccJeff Sharkey    }
154c0622bf896c1af62b0e69b18cd84c7de3b67beb3Jeff Sharkey}
155