1dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz/* 2dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* Copyright (C) 2014 Samsung System LSI 3dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* Licensed under the Apache License, Version 2.0 (the "License"); 4dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* you may not use this file except in compliance with the License. 5dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* You may obtain a copy of the License at 6dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* 7dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* http://www.apache.org/licenses/LICENSE-2.0 8dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* 9dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* Unless required by applicable law or agreed to in writing, software 10dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* distributed under the License is distributed on an "AS IS" BASIS, 11dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* See the License for the specific language governing permissions and 13dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz* limitations under the License. 14dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz*/ 15dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzpackage com.android.bluetooth.map; 16dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 17dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport android.annotation.TargetApi; 18dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport android.content.ContentProvider; 19dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport android.content.ContentValues; 20dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport android.database.Cursor; 21dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport android.net.Uri; 22dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport android.os.Bundle; 23dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport android.os.ParcelFileDescriptor; 24dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport android.provider.Telephony.Mms; 25dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport android.util.Log; 26dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 27dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport com.google.android.mms.MmsException; 28dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport com.google.android.mms.pdu.GenericPdu; 29dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport com.google.android.mms.pdu.PduComposer; 30dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport com.google.android.mms.pdu.PduPersister; 31dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 32dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport java.io.FileNotFoundException; 33dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport java.io.FileOutputStream; 34dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport java.io.IOException; 35dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzimport java.net.URI; 36dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 37dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz/** 38dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz * Provider to let the MMS subsystem read data from it own database from another process. 39dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz * Workaround for missing access to sendStoredMessage(). 40dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz */ 41dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz@TargetApi(19) 42dbe4c2517804b3e32587310b76f1c977b8749d8bkschulzpublic class MmsFileProvider extends ContentProvider { 43dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz static final String TAG = "BluetoothMmsFileProvider"; 44dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz private PipeWriter mPipeWriter = new PipeWriter(); 45dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 46dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz /*package*/ 47dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz static final Uri CONTENT_URI = Uri.parse("content://com.android.bluetooth.map.MmsFileProvider"); 48dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 49dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz @Override 50dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz public boolean onCreate() { 51dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz return true; 52dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 53dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 54dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz @Override 55dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 56dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz String sortOrder) { 57dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz // Don't support queries. 58dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz return null; 59dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 60dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 61dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz @Override 62dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz public Uri insert(Uri uri, ContentValues values) { 63dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz // Don't support inserts. 64dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz return null; 65dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 66dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 67dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz @Override 68dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz public int delete(Uri uri, String selection, String[] selectionArgs) { 69dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz // Don't support deletes. 70dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz return 0; 71dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 72dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 73dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz @Override 74dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 75dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz // Don't support updates. 76dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz return 0; 77dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 78dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 79dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz @Override 80dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz public String getType(Uri uri) { 81dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz // For this sample, assume all files have no type. 82dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz return null; 83dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 84dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 85dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz @Override 86dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz public ParcelFileDescriptor openFile(Uri uri, String fileMode) throws FileNotFoundException { 87dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz String idStr = uri.getLastPathSegment(); 88dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz if(idStr == null) { 89dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz throw new FileNotFoundException("Unable to extract message handle from: " + uri); 90dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 91dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz try { 92dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz long id = Long.parseLong(idStr); 93dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } catch (NumberFormatException e) { 94dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz Log.w(TAG,e); 95dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz throw new FileNotFoundException("Unable to extract message handle from: " + uri); 96dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 97dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz Uri messageUri = Mms.CONTENT_URI.buildUpon().appendEncodedPath(idStr).build(); 98dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 99dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz return openPipeHelper (messageUri, null, null, null, mPipeWriter); 100dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 101dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 102dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 103dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz public class PipeWriter implements PipeDataWriter<Cursor> { 104dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz /** 105dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz * Generate a message based on the cursor, and write the encoded data to the stream. 106dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz */ 107dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 108dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, 109dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz Bundle opts, Cursor c) { 110dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz if (BluetoothMapService.DEBUG) Log.d(TAG, "writeDataToPipe(): uri=" + uri.toString() + 111dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz " - getLastPathSegment() = " + uri.getLastPathSegment()); 112dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 113dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz FileOutputStream fout = null; 114dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz GenericPdu pdu = null; 115dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz PduPersister pduPersister = null; 116dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 117dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz try { 118dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz fout = new FileOutputStream(output.getFileDescriptor()); 119dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz pduPersister = PduPersister.getPduPersister(getContext()); 120dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz pdu = pduPersister.load(uri); 121dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz byte[] bytes = (new PduComposer(getContext(), pdu)).make(); 122dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz fout.write(bytes); 123dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 124dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } catch (IOException e) { 125dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz Log.w(TAG, e); 126dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe 127dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz * to throw IOException? 128dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz */ 129dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } catch (MmsException e) { 130dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz Log.w(TAG, e); 131dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz /* TODO: How to signal the error to the calling entity? Had expected writeDataToPipe 132dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz * to throw IOException? 133dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz */ 134dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } finally { 135dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz if(pduPersister != null) pduPersister.release(); 136dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz try { 137dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz fout.flush(); 138dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } catch (IOException e) { 139dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz Log.w(TAG, "IOException: ", e); 140dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 141dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz try { 142dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz fout.close(); 143dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } catch (IOException e) { 144dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz Log.w(TAG, "IOException: ", e); 145dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 146dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 147dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 148dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz } 149dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 150dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz 151dbe4c2517804b3e32587310b76f1c977b8749d8bkschulz} 152