Merge "Add controls in dev options to quarantine apps." into main

This commit is contained in:
Sudheer Shanka
2023-09-21 22:47:30 +00:00
committed by Android (Google) Code Review
14 changed files with 857 additions and 1 deletions

View File

@@ -112,6 +112,7 @@ android_library {
"androidx.room_room-runtime",
"SystemUIUnfoldLib",
"aconfig_settings_flags_lib",
"android.content.pm.flags-aconfig-java",
],
plugins: [

View File

@@ -12470,4 +12470,7 @@
<string name="grammatical_gender_title">Grammatical gender</string>
<!-- Developer settings: select Grammatical gender dialog title [CHAR LIMIT=50]-->
<string name="grammatical_gender_dialog_title">Select Grammatical gender</string>
<!-- Developer settings: Title for the screen allowing user to control Quarantined apps [CHAR LIMIT=50] -->
<string name="quarantined_apps_title">Quarantined Apps</string>
</resources>

View File

@@ -736,6 +736,12 @@
android:title="@string/enable_notes_role_title"
android:summary="@string/enable_notes_role_summary" />
<Preference
android:key="quarantined_apps"
android:title="@string/quarantined_apps_title"
settings:controller="com.android.settings.development.quarantine.QuarantinedAppsPreferenceController"
android:fragment="com.android.settings.development.quarantine.QuarantinedAppsFragment" />
</PreferenceCategory>
<PreferenceCategory

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="quarantined_apps_screen"
android:title="@string/quarantined_apps_title"
settings:controller="com.android.settings.development.quarantine.QuarantinedAppsScreenController"
settings:searchable="true">
</PreferenceScreen>

View File

@@ -0,0 +1,4 @@
# Bug component: 316234
sudheersai@google.com
yamasani@google.com

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development.quarantine;
import android.content.Context;
import android.graphics.drawable.Drawable;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.AppSwitchPreference;
public class QuarantinedAppPreference extends AppSwitchPreference {
private final AppEntry mEntry;
private Drawable mCacheIcon;
public QuarantinedAppPreference(Context context, AppEntry entry) {
super(context);
mEntry = entry;
mCacheIcon = AppUtils.getIconFromCache(mEntry);
mEntry.ensureLabel(context);
setKey(generateKey(mEntry));
if (mCacheIcon != null) {
setIcon(mCacheIcon);
} else {
setIcon(R.drawable.empty_icon);
}
updateState();
}
static String generateKey(AppEntry entry) {
return entry.info.packageName + "|" + entry.info.uid;
}
public AppEntry getEntry() {
return mEntry;
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
if (mCacheIcon == null) {
ThreadUtils.postOnBackgroundThread(() -> {
final Drawable icon = AppUtils.getIcon(getContext(), mEntry);
ThreadUtils.postOnMainThread(() -> {
setIcon(icon);
mCacheIcon = icon;
});
});
}
super.onBindViewHolder(holder);
}
void updateState() {
setTitle(mEntry.label);
setChecked((boolean) mEntry.extraInfo);
notifyChanged();
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development.quarantine;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import com.android.settings.applications.AppStateBaseBridge;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import java.util.ArrayList;
public class QuarantinedAppStateBridge extends AppStateBaseBridge {
private Context mContext;
public QuarantinedAppStateBridge(Context context,
ApplicationsState appState, Callback callback) {
super(appState, callback);
mContext = context;
}
@Override
protected void loadAllExtraInfo() {
final ArrayList<AppEntry> apps = mAppSession.getAllApps();
for (int i = 0; i < apps.size(); i++) {
final AppEntry app = apps.get(i);
updateExtraInfo(app, app.info.packageName, app.info.uid);
}
}
@Override
protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
app.extraInfo = isPackageQuarantined(pkg, uid);
}
private boolean isPackageQuarantined(String pkg, int uid) {
final PackageManager pm = mContext.createContextAsUser(
UserHandle.getUserHandleForUid(uid), 0).getPackageManager();
try {
return pm.isPackageQuarantined(pkg);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development.quarantine;
import static android.view.MenuItem.SHOW_AS_ACTION_ALWAYS;
import static android.view.MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.SearchView;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.applications.AppIconCacheManager;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
import com.android.settingslib.search.SearchIndexable;
import com.google.android.material.appbar.AppBarLayout;
// TODO: b/297934650 - Update this to use SPA framework
@SearchIndexable
public class QuarantinedAppsFragment extends DashboardFragment implements
SearchView.OnQueryTextListener, SearchView.OnCloseListener,
MenuItem.OnActionExpandListener {
private static final String TAG = "QuarantinedApps";
private static final int MENU_SEARCH_APPS = Menu.FIRST + 42;
private static final int MENU_SHOW_SYSTEM = Menu.FIRST + 43;
private static final String EXTRA_SHOW_SYSTEM = "show_system";
private boolean mShowSystem;
private SearchView mSearchView;
private String mCurQuery;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mShowSystem = icicle != null && icicle.getBoolean(EXTRA_SHOW_SYSTEM);
use(QuarantinedAppsScreenController.class).setFilter(mCustomFilter);
use(QuarantinedAppsScreenController.class).setSession(getSettingsLifecycle());
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
mSearchView = new SearchView(getContext());
mSearchView.setOnQueryTextListener(this);
mSearchView.setOnCloseListener(this);
mSearchView.setIconifiedByDefault(true);
menu.add(Menu.NONE, MENU_SEARCH_APPS, Menu.NONE, R.string.search_settings)
.setIcon(R.drawable.ic_find_in_page_24px)
.setActionView(mSearchView)
.setOnActionExpandListener(this)
.setShowAsAction(SHOW_AS_ACTION_ALWAYS | SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE,
mShowSystem ? R.string.menu_hide_system : R.string.menu_show_system);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == MENU_SHOW_SYSTEM) {
mShowSystem = !mShowSystem;
item.setTitle(mShowSystem ? R.string.menu_hide_system : R.string.menu_show_system);
use(QuarantinedAppsScreenController.class).setFilter(mCustomFilter);
use(QuarantinedAppsScreenController.class).rebuild();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onQueryTextChange(String newText) {
mCurQuery = !TextUtils.isEmpty(newText) ? newText : null;
use(QuarantinedAppsScreenController.class).rebuild();
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
// Don't care about this.
return true;
}
@Override
public boolean onClose() {
if (!TextUtils.isEmpty(mSearchView.getQuery())) {
mSearchView.setQuery(null, true);
}
return true;
}
public final AppFilter mCustomFilter = new AppFilter() {
@Override
public void init() {
}
@Override
public boolean filterApp(ApplicationsState.AppEntry entry) {
final AppFilter defaultFilter = mShowSystem ? ApplicationsState.FILTER_ALL_ENABLED
: ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER;
return defaultFilter.filterApp(entry) && (mCurQuery == null
|| entry.label.toLowerCase().contains(mCurQuery.toLowerCase()));
}
};
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
final AppBarLayout mAppBarLayout = getActivity().findViewById(R.id.app_bar);
// To prevent a large space on tool bar.
mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
final AppBarLayout mAppBarLayout = getActivity().findViewById(R.id.app_bar);
// To prevent a large space on tool bar.
mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
return true;
}
@Override
public int getPreferenceScreenResId() {
return R.xml.quarantined_apps;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.QUARANTINED_APPS_DEV_CONTROL;
}
@Override
public void onDestroyView() {
super.onDestroyView();
AppIconCacheManager.getInstance().release();
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.quarantined_apps);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development.quarantine;
import android.content.Context;
import android.content.pm.Flags;
import com.android.settings.core.BasePreferenceController;
public class QuarantinedAppsPreferenceController extends BasePreferenceController {
public QuarantinedAppsPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return Flags.quarantinedEnabled() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
}

View File

@@ -0,0 +1,221 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development.quarantine;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.applications.AppStateBaseBridge;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.ArrayList;
import java.util.Set;
import java.util.TreeSet;
public class QuarantinedAppsScreenController extends BasePreferenceController implements
LifecycleObserver, OnStart, OnStop, OnDestroy,
ApplicationsState.Callbacks, Preference.OnPreferenceChangeListener,
AppStateBaseBridge.Callback {
private final ApplicationsState mApplicationsState;
private final QuarantinedAppStateBridge mQuarantinedAppStateBridge;
private ApplicationsState.Session mSession;
private PreferenceScreen mScreen;
private AppFilter mFilter;
private boolean mExtraLoaded;
public QuarantinedAppsScreenController(Context context, String preferenceKey) {
super(context, preferenceKey);
mApplicationsState = ApplicationsState.getInstance(
(Application) context.getApplicationContext());
mQuarantinedAppStateBridge = new QuarantinedAppStateBridge(context,
mApplicationsState, this);
}
@Override
public void onStart() {
mQuarantinedAppStateBridge.resume(true /* forceLoadAllApps */);
}
@Override
public void onStop() {
mQuarantinedAppStateBridge.pause();
}
@Override
public void onDestroy() {
mQuarantinedAppStateBridge.release();
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mScreen = screen;
}
public void setFilter(AppFilter filter) {
mFilter = filter;
}
public void setSession(Lifecycle lifecycle) {
mSession = mApplicationsState.newSession(this, lifecycle);
}
@Override
public void onExtraInfoUpdated() {
mExtraLoaded = true;
rebuild();
}
public void rebuild() {
if (!mExtraLoaded || mSession == null) {
return;
}
final ArrayList<AppEntry> apps = mSession.rebuild(mFilter,
ApplicationsState.ALPHA_COMPARATOR);
if (apps != null) {
onRebuildComplete(apps);
}
}
@Override
public void onRebuildComplete(ArrayList<AppEntry> apps) {
if (apps == null) {
return;
}
// Preload top visible icons of app list.
AppUtils.preloadTopIcons(mContext, apps,
mContext.getResources().getInteger(R.integer.config_num_visible_app_icons));
// Create apps key set for removing useless preferences
final Set<String> appsKeySet = new TreeSet<>();
// Add or update preferences
final int count = apps.size();
for (int i = 0; i < count; i++) {
final AppEntry entry = apps.get(i);
if (!shouldAddPreference(entry)) {
continue;
}
final String prefkey = QuarantinedAppPreference.generateKey(entry);
appsKeySet.add(prefkey);
QuarantinedAppPreference preference = mScreen.findPreference(prefkey);
if (preference == null) {
preference = new QuarantinedAppPreference(mScreen.getContext(), entry);
preference.setOnPreferenceChangeListener(this);
mScreen.addPreference(preference);
} else {
preference.updateState();
}
preference.setOrder(i);
}
// Remove useless preferences
removeUselessPrefs(appsKeySet);
}
private void removeUselessPrefs(final Set<String> appsKeySet) {
final int prefCount = mScreen.getPreferenceCount();
String prefKey;
if (prefCount > 0) {
for (int i = prefCount - 1; i >= 0; i--) {
final Preference pref = mScreen.getPreference(i);
prefKey = pref.getKey();
if (!appsKeySet.isEmpty() && appsKeySet.contains(prefKey)) {
continue;
}
mScreen.removePreference(pref);
}
}
}
@VisibleForTesting
static boolean shouldAddPreference(AppEntry app) {
return app != null && UserHandle.isApp(app.info.uid);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference instanceof QuarantinedAppPreference) {
final QuarantinedAppPreference quarantinedPreference =
(QuarantinedAppPreference) preference;
final boolean quarantined = newValue == Boolean.TRUE;
setPackageQuarantined(quarantinedPreference.getEntry().info.packageName,
quarantinedPreference.getEntry().info.uid, quarantined);
quarantinedPreference.getEntry().extraInfo = quarantined;
return true;
}
return false;
}
private void setPackageQuarantined(String pkg, int uid, boolean quarantined) {
final PackageManager pm = mContext.createContextAsUser(
UserHandle.getUserHandleForUid(uid), 0).getPackageManager();
pm.setPackagesSuspended(new String[] {pkg}, quarantined, null /* appExtras */,
null /* launcherExtras */, null /* dialogInfo */,
PackageManager.FLAG_SUSPEND_QUARANTINED);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void onRunningStateChanged(boolean running) {
}
@Override
public void onPackageListChanged() {
}
@Override
public void onPackageIconChanged() {
}
@Override
public void onPackageSizeChanged(String packageName) {
}
@Override
public void onAllSizesComputed() {
}
@Override
public void onLauncherInfoChanged() {
}
@Override
public void onLoadEntriesCompleted() {
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development.quarantine;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class QuarantinedAppsScreenControllerTest {
private static final String PREF_KEY = "quarantined_apps_screen";
private static final String TEST_PACKAGE = "com.example.test.pkg";
private static final int TEST_APP_ID = 1234;
private static final int TEST_USER_ID = 10;
private Context mContext;
private QuarantinedAppsScreenController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mController = new QuarantinedAppsScreenController(mContext, PREF_KEY);
}
@Test
public void testOnPreferenceChange() {
final Context userContext = mock(Context.class);
doReturn(userContext).when(mContext).createContextAsUser(
eq(UserHandle.of(TEST_USER_ID)), anyInt());
final PackageManager packageManager = mock(PackageManager.class);
doReturn(packageManager).when(userContext).getPackageManager();
final AppEntry entry = createAppEntry(TEST_PACKAGE, TEST_APP_ID, TEST_USER_ID);
final QuarantinedAppPreference preference = new QuarantinedAppPreference(mContext, entry);
mController.onPreferenceChange(preference, true);
verify(packageManager).setPackagesSuspended(aryEq(new String[] {TEST_PACKAGE}), eq(true),
any(), any(), any(),
eq(PackageManager.FLAG_SUSPEND_QUARANTINED));
mController.onPreferenceChange(preference, false);
verify(packageManager).setPackagesSuspended(aryEq(new String[] {TEST_PACKAGE}), eq(false),
any(), any(), any(),
eq(PackageManager.FLAG_SUSPEND_QUARANTINED));
}
private AppEntry createAppEntry(String packageName, int appId, int userId) {
final AppEntry entry = mock(AppEntry.class);
entry.info = createApplicationInfo(packageName, appId, userId);
entry.extraInfo = false;
return entry;
}
private ApplicationInfo createApplicationInfo(String packageName, int appId, int userId) {
final ApplicationInfo info = new ApplicationInfo();
info.packageName = packageName;
info.uid = UserHandle.getUid(userId, appId);
return info;
}
}

View File

@@ -27,7 +27,7 @@ android_test {
"platform-test-annotations",
"truth-prebuilt",
"kotlinx_coroutines_test",
"flag-junit-base",
"flag-junit",
// Don't add SettingsLib libraries here - you can use them directly as they are in the
// instrumented Settings app.
],

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development.quarantine;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class QuarantinedAppStateBridgeTest {
private static final String TEST_PACKAGE = "com.example.test.pkg";
private static final int TEST_APP_ID = 1234;
private static final int TEST_USER_ID_1 = 0;
private static final int TEST_USER_ID_2 = 10;
@Mock
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void updateExtraInfo_packageQuarantined() throws Exception {
setPackageQuarantined(TEST_PACKAGE, TEST_USER_ID_1, false);
setPackageQuarantined(TEST_PACKAGE, TEST_USER_ID_2, true);
final QuarantinedAppStateBridge bridge =
new QuarantinedAppStateBridge(mContext, null, null);
final AppEntry entry = mock(AppEntry.class);
bridge.updateExtraInfo(entry, TEST_PACKAGE, UserHandle.getUid(TEST_USER_ID_2, TEST_APP_ID));
assertThat(entry.extraInfo).isEqualTo(true);
}
@Test
public void updateExtraInfo_packageNotQuarantined() throws Exception {
setPackageQuarantined(TEST_PACKAGE, TEST_USER_ID_1, false);
setPackageQuarantined(TEST_PACKAGE, TEST_USER_ID_2, false);
final QuarantinedAppStateBridge bridge =
new QuarantinedAppStateBridge(mContext, null, null);
final AppEntry entry = mock(AppEntry.class);
bridge.updateExtraInfo(entry, TEST_PACKAGE, UserHandle.getUid(TEST_USER_ID_2, TEST_APP_ID));
assertThat(entry.extraInfo).isEqualTo(false);
}
private void setPackageQuarantined(String packageName, int userId, boolean quarantined)
throws Exception {
final Context userContext = mock(Context.class);
when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
.thenReturn(userContext);
final PackageManager packageManager = mock(PackageManager.class);
when(userContext.getPackageManager()).thenReturn(packageManager);
when(packageManager.isPackageQuarantined(packageName)).thenReturn(quarantined);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development.quarantine;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static org.junit.Assert.assertEquals;
import android.content.Context;
import android.content.pm.Flags;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class QuarantinedAppsPreferenceControllerTest {
private static final String PREF_KEY = "quarantined_apps";
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Mock
private Context mContext;
private QuarantinedAppsPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new QuarantinedAppsPreferenceController(mContext, PREF_KEY);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_QUARANTINED_ENABLED)
public void testAvailabilityStatus_flagEnabled() {
assertEquals(mController.getAvailabilityStatus(), AVAILABLE);
}
@Test
@RequiresFlagsDisabled(Flags.FLAG_QUARANTINED_ENABLED)
public void testAvailabilityStatus_flagDisabled() {
assertEquals(mController.getAvailabilityStatus(), CONDITIONALLY_UNAVAILABLE);
}
}