1 /*
2  * Copyright (C) 2016 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.settings.applications;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.ArgumentMatchers.eq;
23 import static org.mockito.Mockito.mock;
24 import static org.mockito.Mockito.verify;
25 import static org.mockito.Mockito.when;
26 
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.PackageInfo;
32 import android.content.pm.PackageManager;
33 import android.net.Uri;
34 import android.os.UserHandle;
35 
36 import androidx.appcompat.app.AlertDialog;
37 import androidx.preference.PreferenceManager;
38 import androidx.preference.PreferenceScreen;
39 
40 import com.android.internal.logging.nano.MetricsProto;
41 import com.android.settings.testutils.FakeFeatureFactory;
42 import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
43 import com.android.settings.testutils.shadow.ShadowSettingsLibUtils;
44 import com.android.settings.widget.EntityHeaderController;
45 import com.android.settingslib.applications.AppUtils;
46 import com.android.settingslib.applications.ApplicationsState;
47 import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
48 import com.android.settingslib.widget.LayoutPreference;
49 
50 import org.junit.After;
51 import org.junit.Before;
52 import org.junit.Rule;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 import org.mockito.Answers;
56 import org.mockito.Mock;
57 import org.mockito.junit.MockitoJUnit;
58 import org.mockito.junit.MockitoRule;
59 import org.robolectric.RobolectricTestRunner;
60 import org.robolectric.RuntimeEnvironment;
61 import org.robolectric.annotation.Config;
62 import org.robolectric.util.ReflectionHelpers;
63 
64 @RunWith(RobolectricTestRunner.class)
65 @Config(shadows = {ShadowEntityHeaderController.class, ShadowSettingsLibUtils.class})
66 public class AppInfoWithHeaderTest {
67     @Rule
68     public final MockitoRule mMockitoRule = MockitoJUnit.rule();
69 
70     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
71     private EntityHeaderController mHeaderController;
72 
73     private FakeFeatureFactory mFactory;
74     private TestFragment mAppInfoWithHeader;
75 
76     @Before
setUp()77     public void setUp() {
78         mFactory = FakeFeatureFactory.setupForTest();
79         when(mFactory.metricsFeatureProvider.getMetricsCategory(any(Object.class)))
80                 .thenReturn(MetricsProto.MetricsEvent.SETTINGS_APP_NOTIF_CATEGORY);
81         mAppInfoWithHeader = new TestFragment();
82         ShadowEntityHeaderController.setUseMock(mHeaderController);
83     }
84 
85     @After
tearDown()86     public void tearDown() {
87         ShadowEntityHeaderController.reset();
88     }
89 
90     @Test
testAppHeaderIsAdded()91     public void testAppHeaderIsAdded() {
92         mAppInfoWithHeader.onActivityCreated(null);
93 
94         verify(mAppInfoWithHeader.mScreen).addPreference(any(LayoutPreference.class));
95     }
96 
97     @Test
packageRemoved_noAppEntry_shouldFinishActivity()98     public void packageRemoved_noAppEntry_shouldFinishActivity() {
99         BroadcastReceiver packageRemovedReceiver =
100                 ReflectionHelpers.getField(mAppInfoWithHeader, "mPackageRemovedReceiver");
101         ReflectionHelpers.setField(mAppInfoWithHeader, "mAppEntry", null);
102 
103         final Intent packageRemovedBroadcast = new Intent();
104         packageRemovedBroadcast.setData(Uri.parse("package:com.android.settings"));
105         packageRemovedReceiver.onReceive(RuntimeEnvironment.application, packageRemovedBroadcast);
106 
107         assertThat(mAppInfoWithHeader.mPackageRemovedCalled).isTrue();
108     }
109 
110     @Test
packageRemoved_appEntryMatchesPackageName_shouldFinishActivity()111     public void packageRemoved_appEntryMatchesPackageName_shouldFinishActivity() {
112         BroadcastReceiver packageRemovedReceiver =
113                 ReflectionHelpers.getField(mAppInfoWithHeader, "mPackageRemovedReceiver");
114         final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
115         entry.info = new ApplicationInfo();
116         entry.info.packageName = "com.android.settings";
117         ReflectionHelpers.setField(mAppInfoWithHeader, "mAppEntry", entry);
118 
119         final Intent packageRemovedBroadcast = new Intent();
120         packageRemovedBroadcast.setData(Uri.parse("package:" + entry.info.packageName));
121         packageRemovedReceiver.onReceive(RuntimeEnvironment.application, packageRemovedBroadcast);
122 
123         assertThat(mAppInfoWithHeader.mPackageRemovedCalled).isTrue();
124     }
125 
126     @Test
noExtraUserHandleInIntent_retrieveAppEntryWithMyUserId()127     public void noExtraUserHandleInIntent_retrieveAppEntryWithMyUserId()
128             throws PackageManager.NameNotFoundException {
129         final String packageName = "com.android.settings";
130 
131         mAppInfoWithHeader.mIntent.setData(Uri.fromParts("package", packageName, null));
132         final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
133         entry.info = new ApplicationInfo();
134         entry.info.packageName = packageName;
135 
136         when(mAppInfoWithHeader.mState.getEntry(packageName,
137                 UserHandle.myUserId())).thenReturn(entry);
138         when(mAppInfoWithHeader.mPm.getPackageInfoAsUser(eq(entry.info.packageName),
139                 any(), eq(UserHandle.myUserId()))).thenReturn(
140                 mAppInfoWithHeader.mPackageInfo);
141 
142         mAppInfoWithHeader.retrieveAppEntry();
143 
144         assertThat(mAppInfoWithHeader.mUserId).isEqualTo(UserHandle.myUserId());
145         assertThat(mAppInfoWithHeader.mPackageInfo).isNotNull();
146         assertThat(mAppInfoWithHeader.mAppEntry).isNotNull();
147     }
148 
149     @Test
extraUserHandleInIntent_retrieveAppEntryWithMyUserId()150     public void extraUserHandleInIntent_retrieveAppEntryWithMyUserId()
151             throws PackageManager.NameNotFoundException {
152         final int USER_ID = 1002;
153         final String packageName = "com.android.settings";
154 
155         mAppInfoWithHeader.mIntent.putExtra(Intent.EXTRA_USER_HANDLE, new UserHandle(USER_ID));
156         mAppInfoWithHeader.mIntent.setData(Uri.fromParts("package",
157                 packageName, null));
158         final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
159         entry.info = new ApplicationInfo();
160         entry.info.packageName = packageName;
161 
162         when(mAppInfoWithHeader.mState.getEntry(packageName, USER_ID)).thenReturn(entry);
163         when(mAppInfoWithHeader.mPm.getPackageInfoAsUser(eq(entry.info.packageName),
164                 any(), eq(USER_ID))).thenReturn(
165                 mAppInfoWithHeader.mPackageInfo);
166 
167         mAppInfoWithHeader.retrieveAppEntry();
168 
169         assertThat(mAppInfoWithHeader.mUserId).isEqualTo(USER_ID);
170         assertThat(mAppInfoWithHeader.mPackageInfo).isNotNull();
171         assertThat(mAppInfoWithHeader.mAppEntry).isNotNull();
172     }
173 
174     public static class TestFragment extends AppInfoWithHeader {
175 
176         PreferenceManager mManager;
177         PreferenceScreen mScreen;
178         Context mShadowContext;
179         boolean mPackageRemovedCalled;
180         Intent mIntent;
181 
TestFragment()182         public TestFragment() {
183             mPm = mock(PackageManager.class);
184             mManager = mock(PreferenceManager.class);
185             mScreen = mock(PreferenceScreen.class);
186             mPackageInfo = new PackageInfo();
187             mPackageInfo.applicationInfo = new ApplicationInfo();
188             mState = mock(ApplicationsState.class);
189             mIntent = new Intent();
190             mShadowContext = RuntimeEnvironment.application;
191             ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
192                     (InstantAppDataProvider) (info -> false));
193             when(mManager.getContext()).thenReturn(mShadowContext);
194         }
195 
196         @Override
getMetricsCategory()197         public int getMetricsCategory() {
198             return 0;
199         }
200 
201         @Override
refreshUi()202         protected boolean refreshUi() {
203             return false;
204         }
205 
206         @Override
createDialog(int id, int errorCode)207         protected AlertDialog createDialog(int id, int errorCode) {
208             return null;
209         }
210 
211         @Override
getPreferenceScreen()212         public PreferenceScreen getPreferenceScreen() {
213             return mScreen;
214         }
215 
216         @Override
getPreferenceManager()217         public PreferenceManager getPreferenceManager() {
218             return mManager;
219         }
220 
221         @Override
getContext()222         public Context getContext() {
223             return mShadowContext;
224         }
225 
226         @Override
onPackageRemoved()227         protected void onPackageRemoved() {
228             mPackageRemovedCalled = true;
229         }
230 
231         @Override
getIntent()232         protected Intent getIntent() {
233             return mIntent;
234         }
235     }
236 }
237