1 /* 2 * Copyright (C) 2014 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.cts.tvproviderperf; 18 19 import android.content.ComponentName; 20 import android.content.ContentProviderOperation; 21 import android.content.ContentProviderResult; 22 import android.content.ContentResolver; 23 import android.content.ContentUris; 24 import android.content.ContentValues; 25 import android.content.OperationApplicationException; 26 import android.content.pm.PackageManager; 27 import android.database.Cursor; 28 import android.cts.util.CtsAndroidTestCase; 29 import android.media.tv.TvContract; 30 import android.media.tv.TvContract.Channels; 31 import android.media.tv.TvContract.Programs; 32 import android.net.Uri; 33 import android.os.RemoteException; 34 35 import com.android.cts.util.MeasureRun; 36 import com.android.cts.util.MeasureTime; 37 import com.android.cts.util.ResultType; 38 import com.android.cts.util.ResultUnit; 39 import com.android.cts.util.ReportLog; 40 import com.android.cts.util.TimeoutReq; 41 import com.android.cts.util.Stat; 42 43 import java.util.ArrayList; 44 import java.util.List; 45 46 /** 47 * Test performance of TvProvider on a device. TvProvider typically handles hundreds of 48 * thousands of records periodically, so it is desirable to have performance under a reasonable 49 * bar. 50 */ 51 public class TvProviderPerfTest extends CtsAndroidTestCase { 52 private ContentResolver mContentResolver; 53 private String mInputId; 54 private boolean mHasTvInputFramework; 55 56 @Override 57 protected void setUp() throws Exception { 58 super.setUp(); 59 mHasTvInputFramework = getContext().getPackageManager().hasSystemFeature( 60 PackageManager.FEATURE_LIVE_TV); 61 if (!mHasTvInputFramework) return; 62 mContentResolver = getContext().getContentResolver(); 63 mInputId = TvContract.buildInputId(new ComponentName(getContext(), getClass())); 64 } 65 66 @Override 67 protected void tearDown() throws Exception { 68 try { 69 if (!mHasTvInputFramework) return; 70 mContentResolver.delete(Programs.CONTENT_URI, null, null); 71 mContentResolver.delete(Channels.CONTENT_URI, null, null); 72 } finally { 73 super.tearDown(); 74 } 75 } 76 77 @TimeoutReq(minutes = 10) 78 public void testChannels() throws Exception { 79 if (!mHasTvInputFramework) return; 80 double[] averages = new double[3]; 81 82 // Insert 83 final ArrayList<ContentProviderOperation> operations = new ArrayList<>(); 84 final int TRANSACTION_SIZE = 1000; 85 final int TRANSACTION_RUNS = 100; 86 double[] applyBatchTimes = MeasureTime.measure(TRANSACTION_RUNS, new MeasureRun() { 87 @Override 88 public void run(int i) { 89 operations.clear(); 90 for (int j = 0; j < TRANSACTION_SIZE; ++j) { 91 ContentValues values = new ContentValues(); 92 values.put(Channels.COLUMN_INPUT_ID, mInputId); 93 values.put(Channels.COLUMN_SERVICE_TYPE, 94 Channels.SERVICE_TYPE_AUDIO_VIDEO); 95 values.put(Channels.COLUMN_TYPE, Channels.TYPE_OTHER); 96 operations.add( 97 ContentProviderOperation.newInsert(Channels.CONTENT_URI) 98 .withValues(values).build()); 99 } 100 try { 101 mContentResolver.applyBatch(TvContract.AUTHORITY, operations); 102 } catch (OperationApplicationException | RemoteException e) { 103 throw new RuntimeException(e); 104 } 105 } 106 }); 107 getReportLog().printArray("Elapsed time for insert: ", 108 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); 109 averages[0] = Stat.getAverage(applyBatchTimes); 110 111 // Update 112 final String[] projection = { Channels._ID }; 113 try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI, 114 projection, null, null, null)) { 115 applyBatchTimes = MeasureTime.measure(TRANSACTION_RUNS, new MeasureRun() { 116 @Override 117 public void run(int i) { 118 operations.clear(); 119 for (int j = 0; j < TRANSACTION_SIZE && cursor.moveToNext(); ++j) { 120 Uri channelUri = TvContract.buildChannelUri(cursor.getLong(0)); 121 String number = Integer.toString(i * TRANSACTION_SIZE + j); 122 operations.add( 123 ContentProviderOperation.newUpdate(channelUri) 124 .withValue(Channels.COLUMN_DISPLAY_NUMBER, number) 125 .build()); 126 } 127 try { 128 mContentResolver.applyBatch(TvContract.AUTHORITY, operations); 129 } catch (OperationApplicationException | RemoteException e) { 130 throw new RuntimeException(e); 131 } 132 } 133 }); 134 } 135 getReportLog().printArray("Elapsed time for update: ", 136 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); 137 averages[1] = Stat.getAverage(applyBatchTimes); 138 139 // Delete 140 applyBatchTimes = MeasureTime.measure(1, new MeasureRun() { 141 @Override 142 public void run(int i) { 143 mContentResolver.delete(TvContract.buildChannelsUriForInput(mInputId), null, null); 144 } 145 }); 146 getReportLog().printArray("Elapsed time for delete: ", 147 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); 148 averages[2] = Stat.getAverage(applyBatchTimes); 149 150 // Query is not interesting for channels. 151 getReportLog().printArray("Average elapsed time for (insert, update, delete): ", 152 averages, ResultType.LOWER_BETTER, ResultUnit.MS); 153 } 154 155 @TimeoutReq(minutes = 15) 156 public void testPrograms() throws Exception { 157 if (!mHasTvInputFramework) return; 158 double[] averages = new double[5]; 159 160 // Prepare (insert channels) 161 final ArrayList<ContentProviderOperation> operations = new ArrayList<>(); 162 final int TRANSACTION_SIZE = 1000; 163 final int NUM_CHANNELS = 100; 164 final List<Uri> channelUris = new ArrayList<>(); 165 166 operations.clear(); 167 for (int i = 0; i < NUM_CHANNELS; ++i) { 168 ContentValues values = new ContentValues(); 169 values.put(Channels.COLUMN_INPUT_ID, mInputId); 170 values.put(Channels.COLUMN_SERVICE_TYPE, 171 Channels.SERVICE_TYPE_AUDIO_VIDEO); 172 values.put(Channels.COLUMN_TYPE, Channels.TYPE_OTHER); 173 operations.add( 174 ContentProviderOperation.newInsert(Channels.CONTENT_URI) 175 .withValues(values).build()); 176 } 177 try { 178 ContentProviderResult[] results = 179 mContentResolver.applyBatch(TvContract.AUTHORITY, operations); 180 for (ContentProviderResult result : results) { 181 channelUris.add(result.uri); 182 } 183 } catch (OperationApplicationException | RemoteException e) { 184 throw new RuntimeException(e); 185 } 186 187 // Insert 188 double[] applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() { 189 @Override 190 public void run(int i) { 191 operations.clear(); 192 Uri channelUri = channelUris.get(i); 193 long channelId = ContentUris.parseId(channelUri); 194 for (int j = 0; j < TRANSACTION_SIZE; ++j) { 195 ContentValues values = new ContentValues(); 196 values.put(Programs.COLUMN_CHANNEL_ID, channelId); 197 operations.add( 198 ContentProviderOperation.newInsert(Programs.CONTENT_URI) 199 .withValues(values).build()); 200 } 201 try { 202 mContentResolver.applyBatch(TvContract.AUTHORITY, operations); 203 } catch (OperationApplicationException | RemoteException e) { 204 throw new RuntimeException(e); 205 } 206 } 207 }); 208 getReportLog().printArray("Elapsed time for insert: ", 209 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); 210 averages[0] = Stat.getAverage(applyBatchTimes); 211 212 // Update 213 final long PROGRAM_DURATION_MS = 60 * 1000; 214 final String[] projection = { Programs._ID }; 215 applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() { 216 @Override 217 public void run(int i) { 218 Uri channelUri = channelUris.get(i); 219 operations.clear(); 220 try (final Cursor cursor = mContentResolver.query( 221 TvContract.buildProgramsUriForChannel(channelUri), 222 projection, null, null, null)) { 223 long startTimeMs = 0; 224 long endTimeMs = 0; 225 while (cursor.moveToNext()) { 226 Uri programUri = TvContract.buildProgramUri(cursor.getLong(0)); 227 endTimeMs += PROGRAM_DURATION_MS; 228 operations.add( 229 ContentProviderOperation.newUpdate(programUri) 230 .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, startTimeMs) 231 .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, endTimeMs) 232 .build()); 233 startTimeMs = endTimeMs; 234 } 235 } 236 try { 237 mContentResolver.applyBatch(TvContract.AUTHORITY, operations); 238 } catch (OperationApplicationException | RemoteException e) { 239 throw new RuntimeException(e); 240 } 241 } 242 }); 243 getReportLog().printArray("Elapsed time for update: ", 244 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); 245 averages[1] = Stat.getAverage(applyBatchTimes); 246 247 // Query 248 applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() { 249 @Override 250 public void run(int i) { 251 Uri channelUri = channelUris.get(i); 252 int j = 0; 253 try (final Cursor cursor = mContentResolver.query( 254 TvContract.buildProgramsUriForChannel( 255 channelUri, 0, 256 PROGRAM_DURATION_MS * TRANSACTION_SIZE / 2), 257 projection, null, null, null)) { 258 while (cursor.moveToNext()) { 259 ++j; 260 } 261 } 262 } 263 }); 264 getReportLog().printArray("Elapsed time for query: ", 265 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); 266 averages[2] = Stat.getAverage(applyBatchTimes); 267 268 // Delete programs 269 applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() { 270 @Override 271 public void run(int i) { 272 Uri channelUri = channelUris.get(i); 273 mContentResolver.delete( 274 TvContract.buildProgramsUriForChannel( 275 channelUri, 276 PROGRAM_DURATION_MS * TRANSACTION_SIZE / 2, 277 PROGRAM_DURATION_MS * TRANSACTION_SIZE), 278 null, null); 279 } 280 }); 281 getReportLog().printArray("Elapsed time for delete programs: ", 282 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); 283 averages[3] = Stat.getAverage(applyBatchTimes); 284 285 // Delete channels 286 applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() { 287 @Override 288 public void run(int i) { 289 Uri channelUri = channelUris.get(i); 290 mContentResolver.delete(channelUri, null, null); 291 } 292 }); 293 getReportLog().printArray("Elapsed time for delete channels: ", 294 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); 295 averages[4] = Stat.getAverage(applyBatchTimes); 296 297 getReportLog().printArray("Average elapsed time for (insert, update, query, " 298 + "delete channels, delete programs): ", 299 averages, ResultType.LOWER_BETTER, ResultUnit.MS); 300 } 301 } 302