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.Utils; 30 import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils; 31 32 import com.google.android.mms.MmsException; 33 import com.google.android.mms.pdu.GenericPdu; 34 import com.google.android.mms.pdu.PduComposer; 35 import com.google.android.mms.pdu.PduPersister; 36 37 import java.io.FileNotFoundException; 38 import java.io.FileOutputStream; 39 import java.io.IOException; 40 41 /** 42 * Provider to let the MMS subsystem read data from it own database from another process. Workaround 43 * for missing access to sendStoredMessage(). 44 */ 45 // Next tag value for ContentProfileErrorReportUtils.report(): 5 46 public class MmsFileProvider extends ContentProvider { 47 private static final String TAG = 48 Utils.TAG_PREFIX_BLUETOOTH + MmsFileProvider.class.getSimpleName(); 49 50 private final PipeWriter mPipeWriter = new PipeWriter(); 51 52 /*package*/ 53 static final Uri CONTENT_URI = Uri.parse("content://com.android.bluetooth.map.MmsFileProvider"); 54 55 @Override onCreate()56 public boolean onCreate() { 57 return true; 58 } 59 60 @Override query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)61 public Cursor query( 62 Uri uri, 63 String[] projection, 64 String selection, 65 String[] selectionArgs, 66 String sortOrder) { 67 // Don't support queries. 68 return null; 69 } 70 71 @Override insert(Uri uri, ContentValues values)72 public Uri insert(Uri uri, ContentValues values) { 73 // Don't support inserts. 74 return null; 75 } 76 77 @Override delete(Uri uri, String selection, String[] selectionArgs)78 public int delete(Uri uri, String selection, String[] selectionArgs) { 79 // Don't support deletes. 80 return 0; 81 } 82 83 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)84 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 85 // Don't support updates. 86 return 0; 87 } 88 89 @Override getType(Uri uri)90 public String getType(Uri uri) { 91 // For this sample, assume all files have no type. 92 return null; 93 } 94 95 @Override openFile(Uri uri, String fileMode)96 public ParcelFileDescriptor openFile(Uri uri, String fileMode) throws FileNotFoundException { 97 String idStr = uri.getLastPathSegment(); 98 if (idStr == null) { 99 throw new FileNotFoundException("Unable to extract message handle from: " + uri); 100 } 101 try { 102 Long.parseLong(idStr); 103 } catch (NumberFormatException e) { 104 ContentProfileErrorReportUtils.report( 105 BluetoothProfile.MAP, 106 BluetoothProtoEnums.BLUETOOTH_MMS_FILE_PROVIDER, 107 BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 108 0); 109 Log.w(TAG, e); 110 throw new FileNotFoundException("Unable to extract message handle from: " + uri); 111 } 112 Uri messageUri = Mms.CONTENT_URI.buildUpon().appendEncodedPath(idStr).build(); 113 114 return openPipeHelper(messageUri, null, null, null, mPipeWriter); 115 } 116 117 public class PipeWriter implements PipeDataWriter<Cursor> { 118 /** Generate a message based on the cursor, and write the encoded data to the stream. */ 119 @Override writeDataToPipe( ParcelFileDescriptor output, Uri uri, String mimeType, Bundle opts, Cursor c)120 public void writeDataToPipe( 121 ParcelFileDescriptor output, Uri uri, String mimeType, Bundle opts, Cursor c) { 122 Log.d( 123 TAG, 124 "writeDataToPipe(): uri=" 125 + uri.toString() 126 + " - getLastPathSegment() = " 127 + uri.getLastPathSegment()); 128 129 FileOutputStream fout = null; 130 GenericPdu pdu = null; 131 PduPersister pduPersister = null; 132 133 try { 134 fout = new FileOutputStream(output.getFileDescriptor()); 135 pduPersister = PduPersister.getPduPersister(getContext()); 136 pdu = pduPersister.load(uri); 137 byte[] bytes = (new PduComposer(getContext(), pdu)).make(); 138 fout.write(bytes); 139 140 } catch (IOException e) { 141 ContentProfileErrorReportUtils.report( 142 BluetoothProfile.MAP, 143 BluetoothProtoEnums.BLUETOOTH_MMS_FILE_PROVIDER, 144 BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 145 1); 146 Log.w(TAG, e); 147 /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe 148 * to throw IOException? 149 */ 150 } catch (MmsException e) { 151 ContentProfileErrorReportUtils.report( 152 BluetoothProfile.MAP, 153 BluetoothProtoEnums.BLUETOOTH_MMS_FILE_PROVIDER, 154 BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 155 2); 156 Log.w(TAG, e); 157 /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe 158 * to throw IOException? 159 */ 160 } finally { 161 if (pduPersister != null) { 162 pduPersister.release(); 163 } 164 try { 165 fout.flush(); 166 } catch (IOException e) { 167 ContentProfileErrorReportUtils.report( 168 BluetoothProfile.MAP, 169 BluetoothProtoEnums.BLUETOOTH_MMS_FILE_PROVIDER, 170 BluetoothStatsLog 171 .BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 172 3); 173 Log.w(TAG, "IOException: ", e); 174 } 175 try { 176 fout.close(); 177 } catch (IOException e) { 178 ContentProfileErrorReportUtils.report( 179 BluetoothProfile.MAP, 180 BluetoothProtoEnums.BLUETOOTH_MMS_FILE_PROVIDER, 181 BluetoothStatsLog 182 .BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, 183 4); 184 Log.w(TAG, "IOException: ", e); 185 } 186 } 187 } 188 } 189 } 190