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 107 @Override 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) { 111 Log.d(TAG, "writeDataToPipe(): uri=" + uri.toString() + " - getLastPathSegment() = " 112 + uri.getLastPathSegment()); 113 } 114 115 FileOutputStream fout = null; 116 GenericPdu pdu = null; 117 PduPersister pduPersister = null; 118 119 try { 120 fout = new FileOutputStream(output.getFileDescriptor()); 121 pduPersister = PduPersister.getPduPersister(getContext()); 122 pdu = pduPersister.load(uri); 123 byte[] bytes = (new PduComposer(getContext(), pdu)).make(); 124 fout.write(bytes); 125 126 } catch (IOException e) { 127 Log.w(TAG, e); 128 /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe 129 * to throw IOException? 130 */ 131 } catch (MmsException e) { 132 Log.w(TAG, e); 133 /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe 134 * to throw IOException? 135 */ 136 } finally { 137 if (pduPersister != null) { 138 pduPersister.release(); 139 } 140 try { 141 fout.flush(); 142 } catch (IOException e) { 143 Log.w(TAG, "IOException: ", e); 144 } 145 try { 146 fout.close(); 147 } catch (IOException e) { 148 Log.w(TAG, "IOException: ", e); 149 } 150 } 151 } 152 } 153 154 155 } 156