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