1/* 2 * Copyright (C) 2017 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 17package com.android.providers.tv; 18 19import com.google.android.collect.Sets; 20 21import android.content.ContentUris; 22import android.content.ContentValues; 23import android.content.Context; 24import android.content.Intent; 25import android.content.pm.ProviderInfo; 26import android.database.Cursor; 27import android.media.tv.TvContract; 28import android.media.tv.TvContract.Channels; 29import android.media.tv.TvContract.PreviewPrograms; 30import android.net.Uri; 31import android.os.Bundle; 32import android.provider.Settings; 33import android.test.AndroidTestCase; 34import android.test.mock.MockContentProvider; 35import android.test.mock.MockContentResolver; 36import android.util.Log; 37 38import java.util.ArrayList; 39import java.util.Arrays; 40import java.util.Collection; 41import java.util.HashSet; 42import java.util.Objects; 43import java.util.Set; 44 45public class TransientRowHelperTests extends AndroidTestCase { 46 private static final String FAKE_INPUT_ID = "TransientRowHelperTests"; 47 48 private MockContentResolver mResolver; 49 private TvProviderForTesting mProvider; 50 private RebootSimulatingTransientRowHelper mTransientRowHelper; 51 52 @Override 53 protected void setUp() throws Exception { 54 super.setUp(); 55 56 mResolver = new MockContentResolver(); 57 mResolver.addProvider(Settings.AUTHORITY, new MockContentProvider() { 58 @Override 59 public Bundle call(String method, String request, Bundle args) { 60 return new Bundle(); 61 } 62 }); 63 64 mProvider = new TvProviderForTesting(); 65 mResolver.addProvider(TvContract.AUTHORITY, mProvider); 66 67 setContext(new MockTvProviderContext(mResolver, getContext())); 68 69 final ProviderInfo info = new ProviderInfo(); 70 info.authority = TvContract.AUTHORITY; 71 mProvider.attachInfoForTesting(getContext(), info); 72 mTransientRowHelper = new RebootSimulatingTransientRowHelper(getContext()); 73 mProvider.setTransientRowHelper(mTransientRowHelper); 74 Utils.clearTvProvider(mResolver); 75 } 76 77 @Override 78 protected void tearDown() throws Exception { 79 Utils.clearTvProvider(mResolver); 80 mProvider.shutdown(); 81 super.tearDown(); 82 } 83 84 private static class PreviewProgram { 85 long id; 86 final boolean isTransient; 87 88 PreviewProgram(boolean isTransient) { 89 this(-1, isTransient); 90 } 91 92 PreviewProgram(long id, boolean isTransient) { 93 this.id = id; 94 this.isTransient = isTransient; 95 } 96 97 @Override 98 public boolean equals(Object obj) { 99 if (!(obj instanceof PreviewProgram)) { 100 return false; 101 } 102 PreviewProgram that = (PreviewProgram) obj; 103 return Objects.equals(id, that.id) 104 && Objects.equals(isTransient, that.isTransient); 105 } 106 107 @Override 108 public int hashCode() { 109 return Objects.hash(id, isTransient); 110 } 111 112 @Override 113 public String toString() { 114 return "PreviewProgram(id=" + id + ",isTransient=" + isTransient + ")"; 115 } 116 } 117 118 private long insertChannel(boolean isTransient) { 119 ContentValues values = new ContentValues(); 120 values.put(Channels.COLUMN_INPUT_ID, FAKE_INPUT_ID); 121 values.put(Channels.COLUMN_TRANSIENT, isTransient ? 1 : 0); 122 Uri uri = mResolver.insert(Channels.CONTENT_URI, values); 123 assertNotNull(uri); 124 return ContentUris.parseId(uri); 125 } 126 127 private void insertPreviewPrograms(long channelId, PreviewProgram... programs) { 128 insertPreviewPrograms(channelId, Arrays.asList(programs)); 129 } 130 131 private void insertPreviewPrograms(long channelId, Collection<PreviewProgram> programs) { 132 ContentValues values = new ContentValues(); 133 values.put(PreviewPrograms.COLUMN_CHANNEL_ID, channelId); 134 for (PreviewProgram program : programs) { 135 values.put(PreviewPrograms.COLUMN_TYPE, PreviewPrograms.TYPE_MOVIE); 136 values.put(PreviewPrograms.COLUMN_TRANSIENT, program.isTransient ? 1 : 0); 137 Uri uri = mResolver.insert(PreviewPrograms.CONTENT_URI, values); 138 assertNotNull(uri); 139 program.id = ContentUris.parseId(uri); 140 } 141 } 142 143 private Set<PreviewProgram> queryPreviewPrograms() { 144 String[] projection = new String[] { 145 PreviewPrograms._ID, 146 PreviewPrograms.COLUMN_TRANSIENT, 147 }; 148 149 Cursor cursor = mResolver.query(PreviewPrograms.CONTENT_URI, projection, null, null, null); 150 assertNotNull(cursor); 151 try { 152 Set<PreviewProgram> programs = Sets.newHashSet(); 153 while (cursor.moveToNext()) { 154 programs.add(new PreviewProgram(cursor.getLong(0), cursor.getInt(1) == 1)); 155 } 156 return programs; 157 } finally { 158 cursor.close(); 159 } 160 } 161 162 private long getChannelCount() { 163 String[] projection = new String[] { 164 Channels._ID, 165 }; 166 167 Cursor cursor = mResolver.query(Channels.CONTENT_URI, projection, null, null, null); 168 assertNotNull(cursor); 169 try { 170 return cursor.getCount(); 171 } finally { 172 cursor.close(); 173 } 174 } 175 176 public void testTransientRowsAreDeletedAfterReboot() { 177 PreviewProgram transientProgramInTransientChannel = 178 new PreviewProgram(true /* transient */); 179 PreviewProgram permanentProgramInTransientChannel = 180 new PreviewProgram(false /* transient */); 181 PreviewProgram transientProgramInPermanentChannel = 182 new PreviewProgram(true /* transient */); 183 PreviewProgram permanentProgramInPermanentChannel = 184 new PreviewProgram(false /* transient */); 185 long transientChannelId = insertChannel(true /* transient */); 186 long permanentChannelId = insertChannel(false /* transient */); 187 insertPreviewPrograms(transientChannelId, transientProgramInTransientChannel); 188 insertPreviewPrograms(transientChannelId, permanentProgramInTransientChannel); 189 insertPreviewPrograms(permanentChannelId, transientProgramInPermanentChannel); 190 insertPreviewPrograms(permanentChannelId, permanentProgramInPermanentChannel); 191 192 assertEquals("Before reboot all the programs inserted should exist.", 193 Sets.newHashSet(transientProgramInTransientChannel, 194 permanentProgramInTransientChannel, transientProgramInPermanentChannel, 195 permanentProgramInPermanentChannel), 196 queryPreviewPrograms()); 197 assertEquals("Before reboot the channels inserted should exist.", 198 2, getChannelCount()); 199 200 mTransientRowHelper.simulateReboot(); 201 assertEquals("Transient program and programs in transient channel should be removed.", 202 Sets.newHashSet(permanentProgramInPermanentChannel), queryPreviewPrograms()); 203 assertEquals("Transient channel should not be removed.", 204 1, getChannelCount()); 205 } 206 207 private class RebootSimulatingTransientRowHelper extends TransientRowHelper { 208 private int mLastDeletionBootCount; 209 private int mBootCount = 1; 210 211 private RebootSimulatingTransientRowHelper(Context context) { 212 super(context); 213 } 214 215 @Override 216 protected int getBootCount() { 217 return mBootCount; 218 } 219 220 @Override 221 protected int getLastDeletionBootCount() { 222 return mLastDeletionBootCount; 223 } 224 225 @Override 226 protected void setLastDeletionBootCount() { 227 mLastDeletionBootCount = mBootCount; 228 } 229 230 private void simulateReboot() { 231 mTransientRowsDeleted = false; 232 mBootCount++; 233 } 234 } 235} 236