/* ** DroidPlugin Project ** ** Copyright(c) 2015 Andy Zhang <zhangyong232@gmail.com> ** ** This file is part of DroidPlugin. ** ** DroidPlugin is free software: you can redistribute it and/or ** modify it under the terms of the GNU Lesser General Public ** License as published by the Free Software Foundation, either ** version 3 of the License, or (at your option) any later version. ** ** DroidPlugin is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** Lesser General Public License for more details. ** ** You should have received a copy of the GNU Lesser General Public ** License along with DroidPlugin. If not, see <http://www.gnu.org/licenses/lgpl.txt> ** **/ package com.morgoo.droidplugin.stub; import android.annotation.TargetApi; import android.content.ContentProvider; import android.content.ContentProviderClient; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.database.Cursor; import android.net.Uri; import android.net.Uri.Builder; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Looper; import android.os.RemoteException; import android.text.TextUtils; import com.morgoo.droidplugin.core.Env; import com.morgoo.droidplugin.core.PluginProcessManager; import com.morgoo.droidplugin.pm.PluginManager; import com.morgoo.droidplugin.reflect.FieldUtils; import com.morgoo.helper.Log; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Created by Andy Zhang(zhangyong232@gmail.com) on 2015/2/26. */ public abstract class AbstractContentProviderStub extends ContentProvider { private static final String TAG = AbstractContentProviderStub.class.getSimpleName(); private ContentResolver mContentResolver; private Map<String, ContentProviderClient> sContentProviderClients = new HashMap<String, ContentProviderClient>(); @Override public boolean onCreate() { mContentResolver = getContext().getContentResolver(); return true; } private Uri buildNewUri(Uri uri, String targetAuthority) { Uri.Builder b = new Builder(); b.scheme(uri.getScheme()); b.authority(targetAuthority); b.path(uri.getPath()); if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) { Set<String> names = uri.getQueryParameterNames(); if (names != null && names.size() > 0) { for (String name : names) { if (!TextUtils.equals(name, Env.EXTRA_TARGET_AUTHORITY)) { b.appendQueryParameter(name, uri.getQueryParameter(name)); } } } } else { b.query(uri.getQuery()); } b.fragment(uri.getFragment()); return b.build(); } private synchronized ContentProviderClient getContentProviderClient(final String targetAuthority) { ContentProviderClient client = sContentProviderClients.get(targetAuthority); if (client != null) { return client; } if (Looper.getMainLooper() != Looper.myLooper()) { PluginManager.getInstance().waitForConnected(); } ProviderInfo stubInfo = null; ProviderInfo targetInfo = null; try { String authority = getMyAuthority(); stubInfo = getContext().getPackageManager().resolveContentProvider(authority, 0); targetInfo = PluginManager.getInstance().resolveContentProvider(targetAuthority, 0); } catch (Exception e) { Log.e(TAG, "Can not reportMyProcessName on ContentProvider"); } if (stubInfo != null && targetInfo != null) { try { PluginManager.getInstance().reportMyProcessName(stubInfo.processName, targetInfo.processName, targetInfo.packageName); } catch (RemoteException e) { Log.e(TAG, "RemoteException on reportMyProcessName", e); } } try { if (targetInfo != null) { PluginProcessManager.preLoadApk(getContext(), targetInfo); } } catch (Exception e) { handleExpcetion(e); } ContentProviderClient newClient = mContentResolver.acquireContentProviderClient(targetAuthority); sContentProviderClients.put(targetAuthority, newClient); try { if (stubInfo != null && targetInfo != null) { PluginManager.getInstance().onProviderCreated(stubInfo, targetInfo); } } catch (Exception e) { Log.e(TAG, "Exception on report onProviderCreated", e); } return sContentProviderClients.get(targetAuthority); } private String getMyAuthority() throws PackageManager.NameNotFoundException, IllegalAccessException { if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { return (String) FieldUtils.readField(this, "mAuthority"); } else { Context context = getContext(); PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS); if (pkgInfo != null && pkgInfo.providers != null && pkgInfo.providers.length > 0) { for (ProviderInfo info : pkgInfo.providers) { if (TextUtils.equals(info.name, getClass().getName())) { return info.authority; } } } } return null; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { String targetAuthority = uri.getQueryParameter(Env.EXTRA_TARGET_AUTHORITY); if (!TextUtils.isEmpty(targetAuthority) && !TextUtils.equals(targetAuthority, uri.getAuthority())) { ContentProviderClient client = getContentProviderClient(targetAuthority); try { return client.query(buildNewUri(uri, targetAuthority), projection, selection, selectionArgs, sortOrder); } catch (RemoteException e) { handleExpcetion(e); } } return null; } protected void handleExpcetion(Exception e) { Log.e(TAG, "handleExpcetion", e); } @Override public String getType(Uri uri) { String targetAuthority = uri.getQueryParameter(Env.EXTRA_TARGET_AUTHORITY); if (!TextUtils.isEmpty(targetAuthority) && !TextUtils.equals(targetAuthority, uri.getAuthority())) { ContentProviderClient client = getContentProviderClient(targetAuthority); try { return client.getType(buildNewUri(uri, targetAuthority)); } catch (RemoteException e) { handleExpcetion(e); } } return null; } @Override public Uri insert(Uri uri, ContentValues contentValues) { String targetAuthority = uri.getQueryParameter(Env.EXTRA_TARGET_AUTHORITY); if (!TextUtils.isEmpty(targetAuthority) && !TextUtils.equals(targetAuthority, uri.getAuthority())) { ContentProviderClient client = getContentProviderClient(targetAuthority); try { return client.insert(buildNewUri(uri, targetAuthority), contentValues); } catch (RemoteException e) { handleExpcetion(e); } } return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { String targetAuthority = uri.getQueryParameter(Env.EXTRA_TARGET_AUTHORITY); if (!TextUtils.isEmpty(targetAuthority) && !TextUtils.equals(targetAuthority, uri.getAuthority())) { ContentProviderClient client = getContentProviderClient(targetAuthority); try { return client.delete(buildNewUri(uri, targetAuthority), selection, selectionArgs); } catch (RemoteException e) { handleExpcetion(e); } } return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { String targetAuthority = uri.getQueryParameter(Env.EXTRA_TARGET_AUTHORITY); if (!TextUtils.isEmpty(targetAuthority) && !TextUtils.equals(targetAuthority, uri.getAuthority())) { ContentProviderClient client = getContentProviderClient(targetAuthority); try { return client.update(buildNewUri(uri, targetAuthority), values, selection, selectionArgs); } catch (RemoteException e) { handleExpcetion(e); } } return 0; } @TargetApi(VERSION_CODES.JELLY_BEAN_MR1) @Override public Bundle call(String method, String arg, Bundle extras) { String targetAuthority = extras != null ? extras.getString(Env.EXTRA_TARGET_AUTHORITY) : null; String targetMethod = extras != null ? extras.getString(Env.EXTRA_TARGET_AUTHORITY) : null; if (!TextUtils.isEmpty(targetMethod) && !TextUtils.equals(targetMethod, method)) { ContentProviderClient client = getContentProviderClient(targetAuthority); try { return client.call(targetMethod, arg, extras); } catch (RemoteException e) { handleExpcetion(e); } } return super.call(method, arg, extras); } @Override public int bulkInsert(Uri uri, ContentValues[] values) { String targetAuthority = uri.getQueryParameter(Env.EXTRA_TARGET_AUTHORITY); if (!TextUtils.isEmpty(targetAuthority) && !TextUtils.equals(targetAuthority, uri.getAuthority())) { ContentProviderClient client = getContentProviderClient(targetAuthority); try { return client.bulkInsert(buildNewUri(uri, targetAuthority), values); } catch (RemoteException e) { handleExpcetion(e); } } return super.bulkInsert(uri, values); } @Override public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { //TODO applyBatch转发 // if (operations != null && operations.size() > 0) { // ArrayList<ContentProviderOperation> OldOperations = new ArrayList<ContentProviderOperation>(); // Map<String, ArrayList<ContentProviderOperation>> newOperations = new HashMap<String, ArrayList<ContentProviderOperation>>(); // for (ContentProviderOperation operation : operations) { // Uri uri = operation.getUri(); // String targetAuthority = uri.getQueryParameter(Env.EXTRA_TARGET_AUTHORITY); // if (!TextUtils.isEmpty(targetAuthority) && !TextUtils.equals(targetAuthority, uri.getAuthority())) { // try { // Uri newUri = buildNewUri(uri, targetAuthority); // FieldUtils.writeField(operation, "mUri", newUri, true); // ArrayList<ContentProviderOperation> newOps = newOperations.get(targetAuthority); // if (newOps == null) { // newOps = new ArrayList<ContentProviderOperation>(1); // newOps.add(operation); // newOperations.put(targetAuthority, newOps); // } else { // newOps.add(operation); // } // } catch (IllegalAccessException e) { // handleExpcetion(e); // } // } else { // OldOperations.add(operation); // } // } // // if (newOperations.size() > 0) { // ArrayList<ContentProviderResult> results = new ArrayList<ContentProviderResult>(operations.size()); // for (String authority : newOperations.keySet()) { // ContentProviderClient client = getContentProviderClient(authority); // ArrayList<ContentProviderOperation> contentProviderOperations = newOperations.get(authority); // if (contentProviderOperations.size() > 0) { // ContentProviderResult[] rs = client.applyBatch(contentProviderOperations); // for (ContentProviderResult r : rs) { // results.add(r); // } // } // } // //这一步必须要在主线程中执行,这里还有bug // // } // } return super.applyBatch(operations); } }