10842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu/*
20842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu * Copyright (C) 2017 The Android Open Source Project
30842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu *
40842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu * Licensed under the Apache License, Version 2.0 (the "License");
50842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu * you may not use this file except in compliance with the License.
60842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu * You may obtain a copy of the License at
70842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu *
80842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu *      http://www.apache.org/licenses/LICENSE-2.0
90842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu *
100842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu * Unless required by applicable law or agreed to in writing, software
110842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu * distributed under the License is distributed on an "AS IS" BASIS,
120842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu * See the License for the specific language governing permissions and
140842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu * limitations under the License
150842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu */
160842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
170842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liupackage android.telephony.mbms;
180842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
190842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport android.annotation.NonNull;
200842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport android.annotation.Nullable;
210842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport android.content.ContentProvider;
220842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport android.content.ContentResolver;
230842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport android.content.ContentValues;
240842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport android.content.Context;
25fcbf24075dca399bbe94979aef770b4aef705db4Hall Liuimport android.content.SharedPreferences;
260842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport android.content.pm.ProviderInfo;
270842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport android.database.Cursor;
280842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport android.net.Uri;
290842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport android.os.ParcelFileDescriptor;
3023d80af5c11de192bdb648642706c43c942be60eHall Liuimport android.telephony.MbmsDownloadSession;
310842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
320842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport java.io.File;
330842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport java.io.FileNotFoundException;
340842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liuimport java.io.IOException;
35fcbf24075dca399bbe94979aef770b4aef705db4Hall Liuimport java.util.Objects;
360842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
370842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu/**
380842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu * @hide
390842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu */
400842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liupublic class MbmsTempFileProvider extends ContentProvider {
41fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu    public static final String TEMP_FILE_ROOT_PREF_FILE_NAME = "MbmsTempFileRootPrefs";
42fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu    public static final String TEMP_FILE_ROOT_PREF_NAME = "mbms_temp_file_root";
430842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
440842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    private String mAuthority;
450842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    private Context mContext;
460842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
470842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    @Override
480842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    public boolean onCreate() {
490842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        return true;
500842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
510842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
520842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    @Override
530842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
540842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            @Nullable String selection, @Nullable String[] selectionArgs,
550842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            @Nullable String sortOrder) {
560842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        throw new UnsupportedOperationException("No querying supported");
570842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
580842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
590842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    @Override
600842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    public String getType(@NonNull Uri uri) {
610842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        // EMBMS temp files can contain arbitrary content.
620842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        return "application/octet-stream";
630842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
640842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
650842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    @Override
660842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
670842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        throw new UnsupportedOperationException("No inserting supported");
680842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
690842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
700842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    @Override
710842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    public int delete(@NonNull Uri uri, @Nullable String selection,
720842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            @Nullable String[] selectionArgs) {
730842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        throw new UnsupportedOperationException("No deleting supported");
740842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
750842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
760842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    @Override
770842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String
780842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            selection, @Nullable String[] selectionArgs) {
790842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        throw new UnsupportedOperationException("No updating supported");
800842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
810842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
820842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    @Override
830842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
840842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        // ContentProvider has already checked granted permissions
850842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        final File file = getFileForUri(mContext, mAuthority, uri);
860842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        final int fileMode = ParcelFileDescriptor.parseMode(mode);
870842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        return ParcelFileDescriptor.open(file, fileMode);
880842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
890842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
900842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    @Override
910842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    public void attachInfo(Context context, ProviderInfo info) {
920842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        super.attachInfo(context, info);
930842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
940842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        // Sanity check our security
950842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        if (info.exported) {
960842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            throw new SecurityException("Provider must not be exported");
970842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        }
980842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        if (!info.grantUriPermissions) {
990842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            throw new SecurityException("Provider must grant uri permissions");
1000842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        }
1010842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1020842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        mAuthority = info.authority;
1030842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        mContext = context;
1040842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
1050842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1060842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    public static Uri getUriForFile(Context context, String authority, File file) {
1070842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        // Get the canonical path of the temp file
1080842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        String filePath;
1090842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        try {
1100842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            filePath = file.getCanonicalPath();
1110842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        } catch (IOException e) {
1120842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            throw new IllegalArgumentException("Could not get canonical path for file " + file);
1130842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        }
1140842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1150842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        // Make sure the temp file is contained in the temp file directory as configured in the
1160842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        // manifest
117fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu        File tempFileDir = getEmbmsTempFileDir(context);
1180842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        if (!MbmsUtils.isContainedIn(tempFileDir, file)) {
1190842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            throw new IllegalArgumentException("File " + file + " is not contained in the temp " +
1200842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu                    "file directory, which is " + tempFileDir);
1210842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        }
1220842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1230842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        // Get the canonical path of the temp file directory
1240842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        String tempFileDirPath;
1250842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        try {
1260842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            tempFileDirPath = tempFileDir.getCanonicalPath();
1270842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        } catch (IOException e) {
1280842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            throw new RuntimeException(
1290842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu                    "Could not get canonical path for temp file root dir " + tempFileDir);
1300842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        }
1310842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1320842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        // Start at first char of path under temp file directory
1330842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        String pathFragment;
1340842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        if (tempFileDirPath.endsWith("/")) {
1350842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            pathFragment = filePath.substring(tempFileDirPath.length());
1360842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        } else {
1370842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            pathFragment = filePath.substring(tempFileDirPath.length() + 1);
1380842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        }
1390842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1400842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        String encodedPath = Uri.encode(pathFragment);
1410842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
1420842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu                .authority(authority).encodedPath(encodedPath).build();
1430842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
1440842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1450842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    public static File getFileForUri(Context context, String authority, Uri uri)
1460842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            throws FileNotFoundException {
1470842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
1480842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            throw new IllegalArgumentException("Uri must have scheme content");
1490842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        }
150fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu        if (!Objects.equals(authority, uri.getAuthority())) {
151fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu            throw new IllegalArgumentException("Uri does not have a matching authority: " +
152fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu                    authority + ", " + uri.getAuthority());
153fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu        }
1540842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1550842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        String relPath = Uri.decode(uri.getEncodedPath());
1560842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        File file;
1570842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        File tempFileDir;
1580842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1590842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        try {
160fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu            tempFileDir = getEmbmsTempFileDir(context).getCanonicalFile();
1610842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            file = new File(tempFileDir, relPath).getCanonicalFile();
1620842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        } catch (IOException e) {
1630842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            throw new FileNotFoundException("Could not resolve paths");
1640842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        }
1650842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1660842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        if (!file.getPath().startsWith(tempFileDir.getPath())) {
1670842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu            throw new SecurityException("Resolved path jumped beyond configured root");
1680842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        }
1690842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1700842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        return file;
1710842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
1720842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu
1730842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    /**
1740842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu     * Returns a File for the directory used to store temp files for this app
1750842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu     */
176fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu    public static File getEmbmsTempFileDir(Context context) {
177fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu        SharedPreferences prefs = context.getSharedPreferences(TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
178fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu        String storedTempFileRoot = prefs.getString(TEMP_FILE_ROOT_PREF_NAME, null);
179fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu        try {
180fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu            if (storedTempFileRoot != null) {
181fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu                return new File(storedTempFileRoot).getCanonicalFile();
182fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu            } else {
183ff6f9862e5dd1b2fd825ed69dc11918a0565fd33Hall Liu                return new File(context.getFilesDir(),
18423d80af5c11de192bdb648642706c43c942be60eHall Liu                        MbmsDownloadSession.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY).getCanonicalFile();
185fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu            }
186fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu        } catch (IOException e) {
187fcbf24075dca399bbe94979aef770b4aef705db4Hall Liu            throw new RuntimeException("Unable to canonicalize temp file root path " + e);
1880842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu        }
1890842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu    }
1900842831e2351fafb7d101ef0bc0d1be04f0852a3Hall Liu}
191