1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.media;
18 
19 import android.content.ContentProvider;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.ContextWrapper;
23 import android.content.pm.ProviderInfo;
24 import android.os.Bundle;
25 import android.os.UserHandle;
26 import android.provider.CloudMediaProvider;
27 import android.provider.MediaStore;
28 import android.provider.Settings;
29 import android.test.mock.MockContentProvider;
30 import android.test.mock.MockContentResolver;
31 
32 import androidx.annotation.NonNull;
33 import androidx.annotation.VisibleForTesting;
34 
35 import com.android.providers.media.cloudproviders.CloudProviderPrimary;
36 import com.android.providers.media.cloudproviders.FlakyCloudProvider;
37 import com.android.providers.media.dao.FileRow;
38 import com.android.providers.media.photopicker.PhotoPickerProvider;
39 import com.android.providers.media.photopicker.PickerSyncController;
40 import com.android.providers.media.util.FileUtils;
41 
42 import java.io.File;
43 import java.util.Optional;
44 
45 /**
46  * Class to support mocking Context class for tests.
47  */
48 public class IsolatedContext extends ContextWrapper {
49     private final File mDir;
50     private final MockContentResolver mResolver;
51     private final MediaProvider mMediaProvider;
52     private final UserHandle mUserHandle;
53     private final FlakyCloudProvider mFlakyCloudProvider;
54 
IsolatedContext(Context base, String tag, boolean asFuseThread)55     public IsolatedContext(Context base, String tag, boolean asFuseThread) {
56         this(base, tag, asFuseThread, base.getUser());
57     }
58 
IsolatedContext(Context base, String tag, boolean asFuseThread, UserHandle userHandle)59     public IsolatedContext(Context base, String tag, boolean asFuseThread,
60             UserHandle userHandle) {
61         this(base, tag, asFuseThread, userHandle, new TestConfigStore());
62     }
63 
IsolatedContext(Context base, String tag, boolean asFuseThread, UserHandle userHandle, ConfigStore configStore)64     public IsolatedContext(Context base, String tag, boolean asFuseThread,
65             UserHandle userHandle, ConfigStore configStore) {
66         super(base);
67         mDir = new File(base.getFilesDir(), tag);
68         mDir.mkdirs();
69         FileUtils.deleteContents(mDir);
70 
71         mResolver = new MockContentResolver(this);
72         mUserHandle = userHandle;
73 
74         mMediaProvider = getMockedMediaProvider(asFuseThread, configStore);
75         attachInfoAndAddProvider(base, mMediaProvider, MediaStore.AUTHORITY);
76 
77         MediaDocumentsProvider documentsProvider = new MediaDocumentsProvider();
78         attachInfoAndAddProvider(base, documentsProvider, MediaDocumentsProvider.AUTHORITY);
79 
80         mResolver.addProvider(Settings.AUTHORITY, new MockContentProvider() {
81             @Override
82             public Bundle call(String method, String request, Bundle args) {
83                 return Bundle.EMPTY;
84             }
85         });
86 
87         PhotoPickerProvider photoPickerProvider = new PhotoPickerProvider();
88         attachInfoAndAddProvider(base, photoPickerProvider,
89                 PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY);
90 
91         final CloudMediaProvider cmp = new CloudProviderPrimary();
92         attachInfoAndAddProvider(base, cmp, CloudProviderPrimary.AUTHORITY);
93 
94         mFlakyCloudProvider = new FlakyCloudProvider();
95         attachInfoAndAddProvider(base, mFlakyCloudProvider, FlakyCloudProvider.AUTHORITY);
96 
97         MediaStore.waitForIdle(mResolver);
98     }
99 
getMockedMediaProvider(boolean asFuseThread, ConfigStore configStore)100     private MediaProvider getMockedMediaProvider(boolean asFuseThread,
101             ConfigStore configStore) {
102         return new MediaProvider() {
103             @Override
104             public boolean isFuseThread() {
105                 return asFuseThread;
106             }
107 
108             @Override
109             protected ConfigStore provideConfigStore() {
110                 return configStore;
111             }
112 
113             @Override
114             protected DatabaseBackupAndRecovery createDatabaseBackupAndRecovery() {
115                 return new TestDatabaseBackupAndRecovery(configStore, getVolumeCache());
116             }
117 
118             @Override
119             protected void storageNativeBootPropertyChangeListener() {
120                 // Ignore this as test app cannot read device config
121             }
122 
123             @Override
124             protected void updateQuotaTypeForUri(@NonNull FileRow row) {
125                 return;
126             }
127         };
128     }
129 
130     @Override
131     public File getDatabasePath(String name) {
132         return new File(mDir, name);
133     }
134 
135     @Override
136     public ContentResolver getContentResolver() {
137         return mResolver;
138     }
139 
140     @Override
141     public UserHandle getUser() {
142         return mUserHandle;
143     }
144 
145     public void setPickerUriResolver(PickerUriResolver resolver) {
146         mMediaProvider.setUriResolver(resolver);
147     }
148 
149     public void attachInfoAndAddProvider(Context base, ContentProvider provider,
150             String authority) {
151         final ProviderInfo info = base.getPackageManager().resolveContentProvider(authority, 0);
152         provider.attachInfo(this, info);
153         mResolver.addProvider(authority, provider);
154     }
155 
156     /**
157      * @return {@link DatabaseHelper} The external database helper used by the test {@link
158      * IsolatedContext}
159      */
160     public DatabaseHelper getExternalDatabase() throws IllegalStateException {
161         Optional<DatabaseHelper> helper =
162                 mMediaProvider.getDatabaseHelper(DatabaseHelper.EXTERNAL_DATABASE_NAME);
163         if (helper.isPresent()) {
164             return helper.get();
165         } else {
166             throw new IllegalStateException("Failed to get Database helper");
167         }
168     }
169 
170     @VisibleForTesting
171     public void setFlakyCloudProviderToFlakeInTheNextRequest() {
172         mFlakyCloudProvider.setToFlakeInTheNextRequest();
173     }
174 
175     @VisibleForTesting
176     public void resetFlakyCloudProviderToNotFlakeInTheNextRequest() {
177         mFlakyCloudProvider.resetToNotFlakeInTheNextRequest();
178     }
179 }
180