1 /* 2 * Copyright (C) 2014 Samsung System LSI 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 package com.android.bluetooth.map; 16 17 import android.bluetooth.BluetoothProfile; 18 import android.bluetooth.BluetoothProtoEnums; 19 import android.content.ContentProvider; 20 import android.content.ContentValues; 21 import android.database.Cursor; 22 import android.net.Uri; 23 import android.os.Bundle; 24 import android.os.ParcelFileDescriptor; 25 import android.provider.Telephony.Mms; 26 import android.util.Log; 27 28 import com.android.bluetooth.BluetoothStatsLog; 29 import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils; 30 31 import com.google.android.mms.MmsException; 32 import com.google.android.mms.pdu.GenericPdu; 33 import com.google.android.mms.pdu.PduComposer; 34 import com.google.android.mms.pdu.PduPersister; 35 36 import java.io.FileNotFoundException; 37 import java.io.FileOutputStream; 38 import java.io.IOException; 39 40 /** 41 * Provider to let the MMS subsystem read data from it own database from another process. Workaround 42 * for missing access to sendStoredMessage(). 43 */ 44 // Next tag value for ContentProfileErrorReportUtils.report(): 5 45 public class MmsFileProvider extends ContentProvider { 46 static final String TAG = "BluetoothMmsFileProvider"; 47 private PipeWriter mPipeWriter = new PipeWriter(); 48 49 /*package*/ 50 static final Uri CONTENT_URI = Uri.parse("content://com.android.bluetooth.map.MmsFileProvider"); 51 52 @Override onCreate()53 public boolean onCreate() { 54 return true; 55 } 56 57 @Override query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)58 public Cursor query( 59 Uri uri, 60 String[] projection, 61 String selection, 62 String[] selectionArgs, 63 String sortOrder) { 64 // Don't support queries. 65 return null; 66 } 67 68 @Override insert(Uri uri, ContentValues values)69 public Uri insert(Uri uri, ContentValues values) { 70 // Don't support inserts. 71 return null; 72 } 73 74 @Override delete(Uri uri, String selection, String[] selectionArgs)75 public int delete(Uri uri, String selection, String[] selectionArgs) { 76 // Don't support deletes. 77 return 0; 78 } 79 80 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)81 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 82 // Don't support updates. 83 return 0; 84 } 85 86 @Override getType(Uri uri)87 public String getType(Uri uri) { 88 // For this sample, assume all files have no type. 89 return null; 90 } 91 92 @Override openFile(Uri uri, String fileMode)93 public ParcelFileDescriptor openFile(Uri uri, String fileMode) throws FileNotFoundException { 94 String idStr = uri.getLastPathSegment(); 95 if (idStr == null) { 96 throw new FileNotFoundException("Unable to extract message handle from: " + uri); 97 } 98 try { 99 Long.parseLong(idStr); 100 } catch (NumberFormatException e) { 101 ContentProfileErrorReportUtils.report( 102 BluetoothProfile.MAP, 103 BluetoothProtoEnums.BLUETOOTH_MMS_FILE_PROVIDER, 104 BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 105 0); 106 Log.w(TAG, e); 107 throw new FileNotFoundException("Unable to extract message handle from: " + uri); 108 } 109 Uri messageUri = Mms.CONTENT_URI.buildUpon().appendEncodedPath(idStr).build(); 110 111 return openPipeHelper(messageUri, null, null, null, mPipeWriter); 112 } 113 114 public class PipeWriter implements PipeDataWriter<Cursor> { 115 /** Generate a message based on the cursor, and write the encoded data to the stream. */ 116 @Override writeDataToPipe( ParcelFileDescriptor output, Uri uri, String mimeType, Bundle opts, Cursor c)117 public void writeDataToPipe( 118 ParcelFileDescriptor output, Uri uri, String mimeType, Bundle opts, Cursor c) { 119 Log.d( 120 TAG, 121 "writeDataToPipe(): uri=" 122 + uri.toString() 123 + " - getLastPathSegment() = " 124 + uri.getLastPathSegment()); 125 126 FileOutputStream fout = null; 127 GenericPdu pdu = null; 128 PduPersister pduPersister = null; 129 130 try { 131 fout = new FileOutputStream(output.getFileDescriptor()); 132 pduPersister = PduPersister.getPduPersister(getContext()); 133 pdu = pduPersister.load(uri); 134 byte[] bytes = (new PduComposer(getContext(), pdu)).make(); 135 fout.write(bytes); 136 137 } catch (IOException e) { 138 ContentProfileErrorReportUtils.report( 139 BluetoothProfile.MAP, 140 BluetoothProtoEnums.BLUETOOTH_MMS_FILE_PROVIDER, 141 BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 142 1); 143 Log.w(TAG, e); 144 /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe 145 * to throw IOException? 146 */ 147 } catch (MmsException e) { 148 ContentProfileErrorReportUtils.report( 149 BluetoothProfile.MAP, 150 BluetoothProtoEnums.BLUETOOTH_MMS_FILE_PROVIDER, 151 BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 152 2); 153 Log.w(TAG, e); 154 /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe 155 * to throw IOException? 156 */ 157 } finally { 158 if (pduPersister != null) { 159 pduPersister.release(); 160 } 161 try { 162 fout.flush(); 163 } catch (IOException e) { 164 ContentProfileErrorReportUtils.report( 165 BluetoothProfile.MAP, 166 BluetoothProtoEnums.BLUETOOTH_MMS_FILE_PROVIDER, 167 BluetoothStatsLog 168 .BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 169 3); 170 Log.w(TAG, "IOException: ", e); 171 } 172 try { 173 fout.close(); 174 } catch (IOException e) { 175 ContentProfileErrorReportUtils.report( 176 BluetoothProfile.MAP, 177 BluetoothProtoEnums.BLUETOOTH_MMS_FILE_PROVIDER, 178 BluetoothStatsLog 179 .BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 180 4); 181 Log.w(TAG, "IOException: ", e); 182 } 183 } 184 } 185 } 186 } 187