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