1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. 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 distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package android.testing; 16 17 import android.content.ContentProvider; 18 import android.content.ContentResolver; 19 import android.content.Context; 20 import android.content.IContentProvider; 21 import android.database.ContentObserver; 22 import android.net.Uri; 23 import android.util.ArrayMap; 24 import android.util.ArraySet; 25 26 import com.google.android.collect.Maps; 27 28 import java.util.Map; 29 30 /** 31 * A version of ContentResolver that allows easy mocking of providers. 32 * By default it acts as a normal ContentResolver and returns all the 33 * same providers. 34 * @see #addProvider(String, ContentProvider) 35 * @see #setFallbackToExisting(boolean) 36 */ 37 public class TestableContentResolver extends ContentResolver { 38 39 public static final int STABLE = 1; 40 public static final int UNSTABLE = 2; 41 42 private final Map<String, ContentProvider> mProviders = new ArrayMap<>(); 43 private final Map<String, ContentProvider> mUnstableProviders = new ArrayMap<>(); 44 private final ContentResolver mParent; 45 private final ArraySet<ContentProvider> mInUse = new ArraySet<>(); 46 private boolean mFallbackToExisting; 47 TestableContentResolver(Context context)48 public TestableContentResolver(Context context) { 49 super(context); 50 mParent = context.getContentResolver(); 51 mFallbackToExisting = true; 52 } 53 54 /** 55 * Sets whether existing providers should be returned when a mock does not exist. 56 * The default is true. 57 */ setFallbackToExisting(boolean fallbackToExisting)58 public void setFallbackToExisting(boolean fallbackToExisting) { 59 mFallbackToExisting = fallbackToExisting; 60 } 61 62 /** 63 * Adds access to a provider based on its authority 64 * 65 * @param name The authority name associated with the provider. 66 * @param provider An instance of {@link android.content.ContentProvider} or one of its 67 * subclasses, or null. 68 */ addProvider(String name, ContentProvider provider)69 public void addProvider(String name, ContentProvider provider) { 70 addProvider(name, provider, STABLE | UNSTABLE); 71 } 72 73 /** 74 * Adds access to a provider based on its authority 75 * 76 * @param name The authority name associated with the provider. 77 * @param provider An instance of {@link android.content.ContentProvider} or one of its 78 * subclasses, or null. 79 */ addProvider(String name, ContentProvider provider, int flags)80 public void addProvider(String name, ContentProvider provider, int flags) { 81 if ((flags & STABLE) != 0) { 82 mProviders.put(name, provider); 83 } 84 if ((flags & UNSTABLE) != 0) { 85 mUnstableProviders.put(name, provider); 86 } 87 } 88 89 @Override acquireProvider(Context context, String name)90 protected IContentProvider acquireProvider(Context context, String name) { 91 final ContentProvider provider = mProviders.get(name); 92 if (provider != null) { 93 return provider.getIContentProvider(); 94 } else { 95 return mFallbackToExisting ? mParent.acquireProvider(name) : null; 96 } 97 } 98 99 @Override acquireExistingProvider(Context context, String name)100 protected IContentProvider acquireExistingProvider(Context context, String name) { 101 final ContentProvider provider = mProviders.get(name); 102 if (provider != null) { 103 return provider.getIContentProvider(); 104 } else { 105 return mFallbackToExisting ? mParent.acquireExistingProvider( 106 new Uri.Builder().authority(name).build()) : null; 107 } 108 } 109 110 @Override releaseProvider(IContentProvider provider)111 public boolean releaseProvider(IContentProvider provider) { 112 if (!mFallbackToExisting) return true; 113 if (mInUse.contains(provider)) { 114 mInUse.remove(provider); 115 return true; 116 } 117 return mParent.releaseProvider(provider); 118 } 119 120 @Override acquireUnstableProvider(Context c, String name)121 protected IContentProvider acquireUnstableProvider(Context c, String name) { 122 final ContentProvider provider = mUnstableProviders.get(name); 123 if (provider != null) { 124 return provider.getIContentProvider(); 125 } else { 126 return mFallbackToExisting ? mParent.acquireUnstableProvider(name) : null; 127 } 128 } 129 130 @Override releaseUnstableProvider(IContentProvider icp)131 public boolean releaseUnstableProvider(IContentProvider icp) { 132 if (!mFallbackToExisting) return true; 133 if (mInUse.contains(icp)) { 134 mInUse.remove(icp); 135 return true; 136 } 137 return mParent.releaseUnstableProvider(icp); 138 } 139 140 @Override unstableProviderDied(IContentProvider icp)141 public void unstableProviderDied(IContentProvider icp) { 142 if (!mFallbackToExisting) return; 143 if (mInUse.contains(icp)) { 144 return; 145 } 146 mParent.unstableProviderDied(icp); 147 } 148 149 @Override notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork)150 public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { 151 if (!mFallbackToExisting) return; 152 if (!mProviders.containsKey(uri.getAuthority()) 153 && !mUnstableProviders.containsKey(uri.getAuthority())) { 154 super.notifyChange(uri, observer, syncToNetwork); 155 } 156 } 157 } 158