11b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen/* 21b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * Copyright (C) 2017 The Android Open Source Project 31b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * 41b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * Licensed under the Apache License, Version 2.0 (the "License"); 51b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * you may not use this file except in compliance with the License. 61b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * You may obtain a copy of the License at 71b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * 81b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * http://www.apache.org/licenses/LICENSE-2.0 91b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * 101b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * Unless required by applicable law or agreed to in writing, software 111b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * distributed under the License is distributed on an "AS IS" BASIS, 121b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * See the License for the specific language governing permissions and 141b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen * limitations under the License. 151b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen */ 161b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 171b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenpackage com.android.providers.tv; 181b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 191b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport com.google.android.collect.Sets; 201b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 211b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.content.ContentUris; 221b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.content.ContentValues; 231b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.content.Intent; 241b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.content.pm.ProviderInfo; 251b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.database.Cursor; 261b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.media.tv.TvContract; 271b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.media.tv.TvContract.Channels; 281b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.media.tv.TvContract.Programs; 291b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.net.Uri; 301b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.os.Bundle; 311b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.provider.Settings; 321b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.test.AndroidTestCase; 331b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.test.mock.MockContentProvider; 341b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport android.test.mock.MockContentResolver; 351b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 361b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport java.util.Arrays; 371b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport java.util.Collection; 381b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport java.util.Objects; 391b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenimport java.util.Set; 401b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 411b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chenpublic class PackageRemovedReceiverTest extends AndroidTestCase { 421b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private static final String FAKE_INPUT_ID = "PackageRemovedReceiverTest"; 431b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 441b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private static final String FAKE_PACKAGE_NAME_1 = "package.removed.receiver.Test1"; 451b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private static final String FAKE_PACKAGE_NAME_2 = "package.removed.receiver.Test2"; 461b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 471b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private MockContentResolver mResolver; 481b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private TvProviderForTesting mProvider; 491b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private PackageRemovedReceiver mReceiver; 501b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 511b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen @Override 521b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen protected void setUp() throws Exception { 531b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen super.setUp(); 541b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 551b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mResolver = new MockContentResolver(); 561b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mResolver.addProvider(Settings.AUTHORITY, new MockContentProvider() { 571b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen @Override 581b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen public Bundle call(String method, String request, Bundle args) { 591b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen return new Bundle(); 601b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 611b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen }); 621b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 631b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mProvider = new TvProviderForTesting(); 641b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mResolver.addProvider(TvContract.AUTHORITY, mProvider); 651b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 661b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen setContext(new MockTvProviderContext(mResolver, getContext())); 671b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 681b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen final ProviderInfo info = new ProviderInfo(); 691b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen info.authority = TvContract.AUTHORITY; 701b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mProvider.attachInfoForTesting(getContext(), info); 711b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 721b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mReceiver = new PackageRemovedReceiver(); 73ecfa5fd8a9d974f26f36132687b554248ce9657aDongwon Kang Utils.clearTvProvider(mResolver); 741b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 751b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 761b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen @Override 771b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen protected void tearDown() throws Exception { 78ecfa5fd8a9d974f26f36132687b554248ce9657aDongwon Kang Utils.clearTvProvider(mResolver); 791b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mProvider.shutdown(); 801b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen super.tearDown(); 811b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 821b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 831b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private static class Program { 841b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen long id; 851b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen final String packageName; 861b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 871b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Program(String pkgName) { 881b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen this(-1, pkgName); 891b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 901b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 911b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Program(long id, String pkgName) { 921b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen this.id = id; 931b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen this.packageName = pkgName; 941b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 951b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 961b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen @Override 971b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen public boolean equals(Object obj) { 981b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen if (!(obj instanceof Program)) { 991b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen return false; 1001b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1011b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Program that = (Program) obj; 1021b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen return Objects.equals(id, that.id) 1031b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen && Objects.equals(packageName, that.packageName); 1041b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1051b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1061b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen @Override 1071b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen public int hashCode() { 1081b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen return Objects.hash(id, packageName); 1091b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1101b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1111b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen @Override 1121b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen public String toString() { 1131b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen return "Program(id=" + id + ",packageName=" + packageName + ")"; 1141b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1151b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1161b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1171b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private long insertChannel() { 1181b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen ContentValues values = new ContentValues(); 1191b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen values.put(Channels.COLUMN_INPUT_ID, FAKE_INPUT_ID); 1201b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Uri uri = mResolver.insert(Channels.CONTENT_URI, values); 1211b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen assertNotNull(uri); 1221b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen return ContentUris.parseId(uri); 1231b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1241b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1251b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private void insertPrograms(long channelId, Program... programs) { 1261b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen insertPrograms(channelId, Arrays.asList(programs)); 1271b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1281b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1291b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private void insertPrograms(long channelId, Collection<Program> programs) { 1301b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen ContentValues values = new ContentValues(); 1311b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen values.put(Programs.COLUMN_CHANNEL_ID, channelId); 1321b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen for (Program program : programs) { 1331b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Uri uri = mResolver.insert(Programs.CONTENT_URI, values); 1341b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen assertNotNull(uri); 1351b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen program.id = ContentUris.parseId(uri); 1361b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1371b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1381b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1391b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private Set<Program> queryPrograms() { 1401b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen String[] projection = new String[] { 1411b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Programs._ID, 1421b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME, 1431b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen }; 1441b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1451b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Cursor cursor = mResolver.query(Programs.CONTENT_URI, projection, null, null, null); 1461b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen assertNotNull(cursor); 1471b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen try { 1481b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Set<Program> programs = Sets.newHashSet(); 1491b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen while (cursor.moveToNext()) { 1501b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen programs.add(new Program(cursor.getLong(0), cursor.getString(1))); 1511b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1521b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen return programs; 1531b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } finally { 1541b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen cursor.close(); 1551b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1561b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1571b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1581b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen private long getChannelCount() { 1591b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen String[] projection = new String[] { 1601b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Channels._ID, 1611b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen }; 1621b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1631b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Cursor cursor = mResolver.query(Channels.CONTENT_URI, projection, null, null, null); 1641b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen assertNotNull(cursor); 1651b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen try { 1661b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen return cursor.getCount(); 1671b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } finally { 1681b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen cursor.close(); 1691b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1701b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 1711b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1721b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen public void testPackageRemoved() { 1731b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Program programInPackage1 = new Program(FAKE_PACKAGE_NAME_1); 1741b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Program programInPackage2 = new Program(FAKE_PACKAGE_NAME_2); 1751b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mProvider.callingPackage = FAKE_PACKAGE_NAME_1; 1761b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen long channelInPackage1Id = insertChannel(); 1771b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen insertPrograms(channelInPackage1Id, programInPackage1); 1781b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mProvider.callingPackage = FAKE_PACKAGE_NAME_2; 1791b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen long channelInPackage2ID = insertChannel(); 1801b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen insertPrograms(channelInPackage2ID, programInPackage2); 1811b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1821b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen assertEquals(Sets.newHashSet(programInPackage1, programInPackage2), queryPrograms()); 1831b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen assertEquals(2, getChannelCount()); 1841b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1851b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mReceiver.onReceive(getContext(), new Intent(Intent.ACTION_PACKAGE_FULLY_REMOVED, 1861b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Uri.parse("package:" + FAKE_PACKAGE_NAME_1))); 1871b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1881b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen assertEquals("Program should be removed if its package is removed.", 1891b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Sets.newHashSet(programInPackage2), queryPrograms()); 1901b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen assertEquals("Channel should be removed if its package is removed.", 1, getChannelCount()); 1911b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1921b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen mReceiver.onReceive(getContext(), new Intent(Intent.ACTION_PACKAGE_FULLY_REMOVED, 1931b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen Uri.parse("package:" + FAKE_PACKAGE_NAME_2))); 1941b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen 1951b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen assertTrue("Program should be removed if its package is removed.", 1961b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen queryPrograms().isEmpty()); 1971b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen assertEquals("Channel should be removed if its package is removed.", 0, getChannelCount()); 1981b63db50e1bb304d7d6d77d3136ee066d14f91f9Conrad Chen } 199ecfa5fd8a9d974f26f36132687b554248ce9657aDongwon Kang} 200