1/*
2 * Copyright (C) 2015 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.managedprovisioning.task;
18
19import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
20import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertTrue;
22import static org.junit.Assert.fail;
23import static org.mockito.Mockito.verify;
24import static org.mockito.Mockito.verifyNoMoreInteractions;
25import static org.mockito.Mockito.when;
26
27import android.content.Context;
28import android.content.pm.IPackageDeleteObserver;
29import android.content.pm.PackageInfo;
30import android.content.pm.PackageManager;
31import android.os.RemoteException;
32import android.support.test.InstrumentationRegistry;
33import android.support.test.filters.SmallTest;
34import android.test.mock.MockPackageManager;
35
36import com.android.managedprovisioning.model.ProvisioningParams;
37import com.android.managedprovisioning.task.nonrequiredapps.NonRequiredAppsLogic;
38
39import java.util.Arrays;
40import java.util.Collections;
41import java.util.HashSet;
42import java.util.Set;
43
44import org.junit.Before;
45import org.junit.Test;
46import org.mockito.Mock;
47import org.mockito.MockitoAnnotations;
48
49@SmallTest
50public class DeleteNonRequiredAppsTaskTest {
51    private static final String TEST_DPC_PACKAGE_NAME = "dpc.package.name";
52    private static final int TEST_USER_ID = 123;
53    private static final ProvisioningParams TEST_PARAMS = new ProvisioningParams.Builder()
54            .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE)
55            .setDeviceAdminPackageName(TEST_DPC_PACKAGE_NAME)
56            .build();
57
58    private @Mock AbstractProvisioningTask.Callback mCallback;
59    private @Mock Context mTestContext;
60    private @Mock NonRequiredAppsLogic mLogic;
61
62    private FakePackageManager mPackageManager;
63    private Set<String> mDeletedApps;
64    private DeleteNonRequiredAppsTask mTask;
65
66    @Before
67    public void setUp() throws Exception {
68        MockitoAnnotations.initMocks(this);
69
70        mPackageManager = new FakePackageManager();
71
72        when(mTestContext.getPackageManager()).thenReturn(mPackageManager);
73        when(mTestContext.getFilesDir()).thenReturn(InstrumentationRegistry.getTargetContext()
74                .getFilesDir());
75
76        mDeletedApps = new HashSet<>();
77
78        mTask = new DeleteNonRequiredAppsTask(mTestContext, TEST_PARAMS, mCallback, mLogic);
79    }
80
81    @Test
82    public void testNoAppsToDelete() {
83        // GIVEN that no apps should be deleted
84        when(mLogic.getSystemAppsToRemove(TEST_USER_ID)).thenReturn(Collections.emptySet());
85        mPackageManager.setInstalledApps(setFromArray("app.a"));
86
87        // WHEN running the task
88        mTask.run(TEST_USER_ID);
89
90        // THEN maybe take snapshot should have been called
91        verify(mLogic).maybeTakeSystemAppsSnapshot(TEST_USER_ID);
92
93        // THEN success should be called
94        verify(mCallback).onSuccess(mTask);
95        verifyNoMoreInteractions(mCallback);
96
97        // THEN no apps should have been deleted
98        assertDeletedApps();
99    }
100
101    @Test
102    public void testAppsToDelete() {
103        // GIVEN that some apps should be deleted
104        when(mLogic.getSystemAppsToRemove(TEST_USER_ID))
105                .thenReturn(setFromArray("app.a", "app.b"));
106        // GIVEN that only app a is currently installed
107        mPackageManager.setInstalledApps(setFromArray("app.a", "app.c"));
108
109        // WHEN running the task
110        mTask.run(TEST_USER_ID);
111
112        // THEN maybe take snapshot should have been called
113        verify(mLogic).maybeTakeSystemAppsSnapshot(TEST_USER_ID);
114
115        // THEN success should be called
116        verify(mCallback).onSuccess(mTask);
117        verifyNoMoreInteractions(mCallback);
118
119        // THEN those apps should have been deleted
120        assertDeletedApps("app.a");
121    }
122
123    @Test
124    public void testAllAppsAlreadyDeleted() {
125        // GIVEN that some apps should be deleted
126        when(mLogic.getSystemAppsToRemove(TEST_USER_ID))
127            .thenReturn(setFromArray("app.a", "app.b"));
128
129        // WHEN running the task
130        mTask.run(TEST_USER_ID);
131
132        // THEN maybe take snapshot should have been called
133        verify(mLogic).maybeTakeSystemAppsSnapshot(TEST_USER_ID);
134
135        // THEN success should be called
136        verify(mCallback).onSuccess(mTask);
137        verifyNoMoreInteractions(mCallback);
138
139        // THEN those apps should have been deleted
140        assertDeletedApps();
141    }
142
143    @Test
144    public void testDeletionFailed() {
145        // GIVEN that one app should be deleted
146        when(mLogic.getSystemAppsToRemove(TEST_USER_ID))
147            .thenReturn(setFromArray("app.a"));
148        mPackageManager.setInstalledApps(setFromArray("app.a"));
149
150        // GIVEN that deletion fails
151        mPackageManager.setDeletionSucceeds(false);
152
153        // WHEN running the task
154        mTask.run(TEST_USER_ID);
155
156        // THEN maybe take snapshot should have been called
157        verify(mLogic).maybeTakeSystemAppsSnapshot(TEST_USER_ID);
158
159        // THEN error should be returned
160        verify(mCallback).onError(mTask, 0);
161        verifyNoMoreInteractions(mCallback);
162    }
163
164    private <T> Set<T> setFromArray(T... array) {
165        if (array == null) {
166            return null;
167        }
168        return new HashSet<>(Arrays.asList(array));
169    }
170
171    private void assertDeletedApps(String... appArray) {
172        assertEquals(setFromArray(appArray), mDeletedApps);
173    }
174
175
176    class FakePackageManager extends MockPackageManager {
177        private boolean mDeletionSucceeds = true;
178        private Set<String> mInstalledApps = new HashSet<>();
179
180        void setDeletionSucceeds(boolean deletionSucceeds) {
181            mDeletionSucceeds = deletionSucceeds;
182        }
183
184        void setInstalledApps(Set<String> set) {
185            mInstalledApps = set;
186        }
187
188        @Override
189        public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer,
190                int flags, int userId) {
191            if (mDeletionSucceeds) {
192                mDeletedApps.add(packageName);
193            }
194            assertTrue((flags & PackageManager.DELETE_SYSTEM_APP) != 0);
195            assertEquals(TEST_USER_ID, userId);
196
197            int resultCode;
198            if (mDeletionSucceeds) {
199                resultCode = PackageManager.DELETE_SUCCEEDED;
200            } else {
201                resultCode = PackageManager.DELETE_FAILED_INTERNAL_ERROR;
202            }
203            assertTrue(mInstalledApps.remove(packageName));
204
205            try {
206                observer.packageDeleted(packageName, resultCode);
207            } catch (RemoteException e) {
208                fail(e.toString());
209            }
210        }
211
212        @Override
213        public PackageInfo getPackageInfoAsUser(String pkg, int flag, int userId)
214                throws NameNotFoundException {
215            if (mInstalledApps.contains(pkg) && userId == TEST_USER_ID) {
216                return new PackageInfo();
217            }
218            throw new NameNotFoundException();
219        }
220    }
221}
222