1 /* 2 * Copyright (C) 2021 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.cloudproviders; 18 19 import static android.provider.CloudMediaProviderContract.EXTRA_PAGE_TOKEN; 20 21 import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator; 22 23 import static java.util.concurrent.TimeUnit.MILLISECONDS; 24 25 import android.content.res.AssetFileDescriptor; 26 import android.database.Cursor; 27 import android.graphics.Point; 28 import android.os.Bundle; 29 import android.os.CancellationSignal; 30 import android.os.ParcelFileDescriptor; 31 import android.provider.CloudMediaProvider; 32 import android.util.Log; 33 34 import androidx.annotation.VisibleForTesting; 35 36 import com.android.providers.media.PickerProviderMediaGenerator; 37 import com.android.providers.media.photopicker.data.CloudProviderQueryExtras; 38 39 import java.io.FileNotFoundException; 40 41 /** 42 * Implements a cloud {@link CloudMediaProvider} interface over items generated with {@link 43 * MediaGenerator} 44 * 45 * <p>This provider is intentionally very flaky and will throw a {@link RuntimeException} for two 46 * out of every three requests. 47 */ 48 public class FlakyCloudProvider extends CloudMediaProvider { 49 private static final String TAG = "FlakyCloudProvider"; 50 public static final String AUTHORITY = 51 "com.android.providers.media.photopicker.tests.cloud_flaky"; 52 public static final String ACCOUNT_NAME = "test_account@flakyCloudProvider"; 53 private static final int INITIAL_REQUEST_COUNT = 0; 54 private static final int REQUEST_COUNT_FOR_NEXT_ONE_TO_FLAKE = 2; 55 56 private final MediaGenerator mMediaGenerator = 57 PickerProviderMediaGenerator.getMediaGenerator(AUTHORITY); 58 59 private int mRequestCount = INITIAL_REQUEST_COUNT; 60 61 /** Determines if the current request should flake. */ shouldFlake()62 private boolean shouldFlake() { 63 64 // Always succeed on the first request. 65 if (++mRequestCount < REQUEST_COUNT_FOR_NEXT_ONE_TO_FLAKE) { 66 return false; 67 } 68 69 if (mRequestCount > REQUEST_COUNT_FOR_NEXT_ONE_TO_FLAKE) { 70 mRequestCount = INITIAL_REQUEST_COUNT; 71 } 72 73 return true; 74 } 75 76 @Override onCreate()77 public boolean onCreate() { 78 mMediaGenerator.setAccountInfo(ACCOUNT_NAME, /* configIntent= */ null); 79 return true; 80 } 81 82 @Override onQueryMedia(Bundle extras)83 public Cursor onQueryMedia(Bundle extras) { 84 final CloudProviderQueryExtras queryExtras = 85 CloudProviderQueryExtras.fromCloudMediaBundle(extras); 86 87 if (shouldFlake()) { 88 throw new RuntimeException("Simulating a crash in FlakyCloudProvider onQueryMedia"); 89 } 90 91 String pageToken = extras.getString(EXTRA_PAGE_TOKEN, null); 92 93 return mMediaGenerator.getMedia( 94 queryExtras.getGeneration(), 95 queryExtras.getAlbumId(), 96 queryExtras.getMimeTypes(), 97 queryExtras.getSizeBytes(), 98 pageToken, 99 queryExtras.getPageSize()); 100 } 101 102 @Override onQueryDeletedMedia(Bundle extras)103 public Cursor onQueryDeletedMedia(Bundle extras) { 104 final CloudProviderQueryExtras queryExtras = 105 CloudProviderQueryExtras.fromCloudMediaBundle(extras); 106 107 if (shouldFlake()) { 108 throw new RuntimeException( 109 "Simulating a crash in FlakyCloudProvider onQueryDeletedMedia"); 110 } 111 112 String pageToken = extras.getString(EXTRA_PAGE_TOKEN, null); 113 114 return mMediaGenerator.getDeletedMedia(queryExtras.getGeneration(), pageToken); 115 } 116 117 @Override onQueryAlbums(Bundle extras)118 public Cursor onQueryAlbums(Bundle extras) { 119 final CloudProviderQueryExtras queryExtras = 120 CloudProviderQueryExtras.fromCloudMediaBundle(extras); 121 122 if (shouldFlake()) { 123 throw new RuntimeException("Simulating a crash in FlakyCloudProvider onQueryAlbums"); 124 } 125 126 String pageToken = extras.getString(EXTRA_PAGE_TOKEN, null); 127 128 return mMediaGenerator.getAlbums( 129 queryExtras.getMimeTypes(), 130 queryExtras.getSizeBytes(), /* isLocal */ 131 false, 132 pageToken); 133 } 134 135 @Override onOpenPreview( String mediaId, Point size, Bundle extras, CancellationSignal signal)136 public AssetFileDescriptor onOpenPreview( 137 String mediaId, Point size, Bundle extras, CancellationSignal signal) 138 throws FileNotFoundException { 139 throw new UnsupportedOperationException("onOpenPreview not supported"); 140 } 141 142 @Override onOpenMedia( String mediaId, Bundle extras, CancellationSignal signal)143 public ParcelFileDescriptor onOpenMedia( 144 String mediaId, Bundle extras, CancellationSignal signal) throws FileNotFoundException { 145 throw new UnsupportedOperationException("onOpenMedia not supported"); 146 } 147 148 @Override onGetMediaCollectionInfo(Bundle extras)149 public Bundle onGetMediaCollectionInfo(Bundle extras) { 150 if (shouldFlake()) { 151 try { 152 MILLISECONDS.sleep(/* timeout= */ 200L); 153 } catch (InterruptedException e) { 154 Log.d(TAG, "Error while sleep when should flake on get media collection info.", e); 155 } 156 } 157 158 return mMediaGenerator.getMediaCollectionInfo(); 159 } 160 161 @VisibleForTesting resetToNotFlakeInTheNextRequest()162 public void resetToNotFlakeInTheNextRequest() { 163 mRequestCount = INITIAL_REQUEST_COUNT; 164 } 165 166 @VisibleForTesting setToFlakeInTheNextRequest()167 public void setToFlakeInTheNextRequest() { 168 mRequestCount = REQUEST_COUNT_FOR_NEXT_ONE_TO_FLAKE; 169 } 170 } 171