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.annotation.TargetApi; 18 import android.content.ContentProvider; 19 import android.content.ContentValues; 20 import android.database.Cursor; 21 import android.net.Uri; 22 import android.os.Bundle; 23 import android.os.ParcelFileDescriptor; 24 import android.provider.Telephony.Mms; 25 import android.util.Log; 26 27 import com.google.android.mms.MmsException; 28 import com.google.android.mms.pdu.GenericPdu; 29 import com.google.android.mms.pdu.PduComposer; 30 import com.google.android.mms.pdu.PduPersister; 31 32 import java.io.FileNotFoundException; 33 import java.io.FileOutputStream; 34 import java.io.IOException; 35 import java.net.URI; 36 37 /** 38 * Provider to let the MMS subsystem read data from it own database from another process. 39 * Workaround for missing access to sendStoredMessage(). 40 */ 41 @TargetApi(19) 42 public class MmsFileProvider extends ContentProvider { 43 static final String TAG = "BluetoothMmsFileProvider"; 44 private PipeWriter mPipeWriter = new PipeWriter(); 45 46 /*package*/ 47 static final Uri CONTENT_URI = Uri.parse("content://com.android.bluetooth.map.MmsFileProvider"); 48 49 @Override onCreate()50 public boolean onCreate() { 51 return true; 52 } 53 54 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)55 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 56 String sortOrder) { 57 // Don't support queries. 58 return null; 59 } 60 61 @Override insert(Uri uri, ContentValues values)62 public Uri insert(Uri uri, ContentValues values) { 63 // Don't support inserts. 64 return null; 65 } 66 67 @Override delete(Uri uri, String selection, String[] selectionArgs)68 public int delete(Uri uri, String selection, String[] selectionArgs) { 69 // Don't support deletes. 70 return 0; 71 } 72 73 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)74 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 75 // Don't support updates. 76 return 0; 77 } 78 79 @Override getType(Uri uri)80 public String getType(Uri uri) { 81 // For this sample, assume all files have no type. 82 return null; 83 } 84 85 @Override openFile(Uri uri, String fileMode)86 public ParcelFileDescriptor openFile(Uri uri, String fileMode) throws FileNotFoundException { 87 String idStr = uri.getLastPathSegment(); 88 if(idStr == null) { 89 throw new FileNotFoundException("Unable to extract message handle from: " + uri); 90 } 91 try { 92 long id = Long.parseLong(idStr); 93 } catch (NumberFormatException e) { 94 Log.w(TAG,e); 95 throw new FileNotFoundException("Unable to extract message handle from: " + uri); 96 } 97 Uri messageUri = Mms.CONTENT_URI.buildUpon().appendEncodedPath(idStr).build(); 98 99 return openPipeHelper (messageUri, null, null, null, mPipeWriter); 100 } 101 102 103 public class PipeWriter implements PipeDataWriter<Cursor> { 104 /** 105 * Generate a message based on the cursor, and write the encoded data to the stream. 106 */ 107 writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, Bundle opts, Cursor c)108 public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, 109 Bundle opts, Cursor c) { 110 if (BluetoothMapService.DEBUG) Log.d(TAG, "writeDataToPipe(): uri=" + uri.toString() + 111 " - getLastPathSegment() = " + uri.getLastPathSegment()); 112 113 FileOutputStream fout = null; 114 GenericPdu pdu = null; 115 PduPersister pduPersister = null; 116 117 try { 118 fout = new FileOutputStream(output.getFileDescriptor()); 119 pduPersister = PduPersister.getPduPersister(getContext()); 120 pdu = pduPersister.load(uri); 121 byte[] bytes = (new PduComposer(getContext(), pdu)).make(); 122 fout.write(bytes); 123 124 } catch (IOException e) { 125 Log.w(TAG, e); 126 /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe 127 * to throw IOException? 128 */ 129 } catch (MmsException e) { 130 Log.w(TAG, e); 131 /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe 132 * to throw IOException? 133 */ 134 } finally { 135 if(pduPersister != null) pduPersister.release(); 136 try { 137 fout.flush(); 138 } catch (IOException e) { 139 Log.w(TAG, "IOException: ", e); 140 } 141 try { 142 fout.close(); 143 } catch (IOException e) { 144 Log.w(TAG, "IOException: ", e); 145 } 146 } 147 } 148 } 149 150 151 } 152