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*/ 15package com.android.bluetooth.map; 16 17import android.annotation.TargetApi; 18import android.content.ContentProvider; 19import android.content.ContentValues; 20import android.database.Cursor; 21import android.net.Uri; 22import android.os.Bundle; 23import android.os.ParcelFileDescriptor; 24import android.provider.Telephony.Mms; 25import android.util.Log; 26 27import com.google.android.mms.MmsException; 28import com.google.android.mms.pdu.GenericPdu; 29import com.google.android.mms.pdu.PduComposer; 30import com.google.android.mms.pdu.PduPersister; 31 32import java.io.FileNotFoundException; 33import java.io.FileOutputStream; 34import java.io.IOException; 35import 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) 42public 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 50 public boolean onCreate() { 51 return true; 52 } 53 54 @Override 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 62 public Uri insert(Uri uri, ContentValues values) { 63 // Don't support inserts. 64 return null; 65 } 66 67 @Override 68 public int delete(Uri uri, String selection, String[] selectionArgs) { 69 // Don't support deletes. 70 return 0; 71 } 72 73 @Override 74 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 75 // Don't support updates. 76 return 0; 77 } 78 79 @Override 80 public String getType(Uri uri) { 81 // For this sample, assume all files have no type. 82 return null; 83 } 84 85 @Override 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 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