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