Fix location services for all profiles

Bug: 330538899
Bug: 333507072
Test: atest LocationInjectedServicesPreferenceControllerTest
Test: manual
Change-Id: Ia42e59b73b9b7c84ecb89082e968b801e1fd1302
This commit is contained in:
Manish Singh
2024-04-09 15:41:26 +00:00
parent cf7cfa6a0c
commit cace44f497
7 changed files with 247 additions and 9 deletions

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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:title="@string/location_services_screen_title"
android:key="location_services_private_profile"
settings:controller="com.android.settings.location.LocationInjectedServicesForPrivateProfilePreferenceController">
</PreferenceScreen>

View File

@@ -20,6 +20,7 @@ import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.location.LocationServices;
import com.android.settings.location.LocationServicesForPrivateProfile;
import com.android.settings.location.LocationServicesForWork;
/**
@@ -34,7 +35,7 @@ public class ProfileSelectLocationServicesFragment extends ProfileSelectFragment
null /* bundle */,
LocationServices::new,
LocationServicesForWork::new,
LocationServices::new);
LocationServicesForPrivateProfile::new);
}
@Override

View File

@@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.location.SettingInjectorService;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -32,6 +33,7 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import java.util.List;
@@ -110,13 +112,28 @@ public abstract class LocationInjectedServiceBasePreferenceController
}
protected Map<Integer, List<Preference>> getLocationServices() {
ArraySet<UserHandle> userHandles = new ArraySet<>();
userHandles.add(UserHandle.of(UserHandle.myUserId()));
// If location access is locked down by device policy then we only show injected settings
// for the primary profile.
final int profileUserId = Utils.getManagedProfileId(mUserManager, UserHandle.myUserId());
final int managedProfileId = Utils.getManagedProfileId(mUserManager, UserHandle.myUserId());
if (managedProfileId != UserHandle.USER_NULL
&& mLocationEnabler.getShareLocationEnforcedAdmin(managedProfileId) == null) {
userHandles.add(UserHandle.of(managedProfileId));
}
if (android.os.Flags.allowPrivateProfile()
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
&& android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()) {
final UserHandle privateProfile = Utils.getProfileOfType(mUserManager,
ProfileSelectFragment.ProfileType.PRIVATE);
if (privateProfile != null && mLocationEnabler
.getShareLocationEnforcedAdmin(privateProfile.getIdentifier()) == null) {
userHandles.add(privateProfile);
}
}
return mInjector.getInjectedSettings(mFragment.getPreferenceManager().getContext(),
(profileUserId != UserHandle.USER_NULL
&& mLocationEnabler.getShareLocationEnforcedAdmin(profileUserId) != null)
? UserHandle.myUserId() : UserHandle.USER_CURRENT);
userHandles);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2024 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.location;
import android.content.Context;
import android.os.UserHandle;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.Utils;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.widget.RestrictedAppPreference;
import java.util.List;
import java.util.Map;
/**
* Retrieve the Location Services used in private profile user.
*/
public class LocationInjectedServicesForPrivateProfilePreferenceController extends
LocationInjectedServiceBasePreferenceController {
public LocationInjectedServicesForPrivateProfilePreferenceController(
Context context, String key) {
super(context, key);
}
@Override
protected void injectLocationServices(PreferenceScreen screen) {
if (!android.os.Flags.allowPrivateProfile()
|| !android.multiuser.Flags.enablePrivateSpaceFeatures()
|| !android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()) {
return;
}
final UserHandle privateProfile = Utils.getProfileOfType(mUserManager,
ProfileSelectFragment.ProfileType.PRIVATE);
if (privateProfile == null) {
return;
}
final Map<Integer, List<Preference>> prefs = getLocationServices();
for (Map.Entry<Integer, List<Preference>> entry : prefs.entrySet()) {
for (Preference pref : entry.getValue()) {
if (pref instanceof RestrictedAppPreference) {
((RestrictedAppPreference) pref).checkRestrictionAndSetDisabled();
}
}
if (entry.getKey() == privateProfile.getIdentifier()) {
LocationSettings.addPreferencesSorted(entry.getValue(), screen);
}
}
}
}

View File

@@ -22,6 +22,7 @@ import android.os.UserHandle;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.Utils;
import com.android.settings.widget.RestrictedAppPreference;
import java.util.List;
@@ -40,6 +41,7 @@ public class LocationInjectedServicesForWorkPreferenceController extends
@Override
protected void injectLocationServices(PreferenceScreen screen) {
final int managedProfileId = Utils.getManagedProfileId(mUserManager, UserHandle.myUserId());
final Map<Integer, List<Preference>> prefs = getLocationServices();
for (Map.Entry<Integer, List<Preference>> entry : prefs.entrySet()) {
for (Preference pref : entry.getValue()) {
@@ -47,7 +49,7 @@ public class LocationInjectedServicesForWorkPreferenceController extends
((RestrictedAppPreference) pref).checkRestrictionAndSetDisabled();
}
}
if (entry.getKey() != UserHandle.myUserId()) {
if (entry.getKey() == managedProfileId) {
LocationSettings.addPreferencesSorted(entry.getValue(), screen);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2024 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.location;
import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
public class LocationServicesForPrivateProfile extends DashboardFragment {
private static final String TAG = "LocationServicesForPrivateProfile";
@Override
public int getMetricsCategory() {
return SettingsEnums.LOCATION_SERVICES_FOR_PRIVATE_PROFILE;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.location_services_private_profile;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
use(LocationInjectedServicesForPrivateProfilePreferenceController.class).init(this);
}
}

View File

@@ -31,8 +31,10 @@ import android.content.Context;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
@@ -50,6 +52,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -65,6 +68,8 @@ import java.util.Map;
public class LocationInjectedServicesPreferenceControllerTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String KEY_LOCATION_SERVICES = "location_service";
@@ -140,8 +145,13 @@ public class LocationInjectedServicesPreferenceControllerTest {
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(componentName);
mController.displayPreference(mScreen);
ArgumentCaptor<ArraySet<UserHandle>> profilesArgumentCaptor =
ArgumentCaptor.forClass(ArraySet.class);
verify(mSettingsInjector).getInjectedSettings(
any(Context.class), eq(UserHandle.myUserId()));
any(Context.class), profilesArgumentCaptor.capture());
assertThat(profilesArgumentCaptor.getValue())
.doesNotContain(UserHandle.of(fakeWorkProfileId));
}
@Test
@@ -149,6 +159,9 @@ public class LocationInjectedServicesPreferenceControllerTest {
final int fakeWorkProfileId = 123;
ShadowUserManager.getShadow().setProfileIdsWithDisabled(
new int[]{UserHandle.myUserId(), fakeWorkProfileId});
ShadowUserManager.getShadow().addProfile(new UserInfo(UserHandle.myUserId(), "", 0));
ShadowUserManager.getShadow().addProfile(new UserInfo(fakeWorkProfileId, "",
UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_PROFILE));
// Mock RestrictedLockUtils.checkIfRestrictionEnforced and let it return null.
// Empty enforcing users.
@@ -159,8 +172,77 @@ public class LocationInjectedServicesPreferenceControllerTest {
enforcingUsers);
mController.displayPreference(mScreen);
ArgumentCaptor<ArraySet<UserHandle>> profilesArgumentCaptor =
ArgumentCaptor.forClass(ArraySet.class);
verify(mSettingsInjector).getInjectedSettings(
any(Context.class), eq(UserHandle.USER_CURRENT));
any(Context.class), profilesArgumentCaptor.capture());
assertThat(profilesArgumentCaptor.getValue()).contains(UserHandle.of(fakeWorkProfileId));
}
@Test
public void privateProfileDisallowShareLocationOn_getParentUserLocationServicesOnly() {
mSetFlagsRule.enableFlags(
android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES,
android.multiuser.Flags.FLAG_HANDLE_INTERLEAVED_SETTINGS_FOR_PRIVATE_SPACE);
final int fakePrivateProfileId = 123;
ShadowUserManager.getShadow().setProfileIdsWithDisabled(
new int[]{UserHandle.myUserId(), fakePrivateProfileId});
ShadowUserManager.getShadow().addProfile(new UserInfo(UserHandle.myUserId(), "", 0));
ShadowUserManager.getShadow().setPrivateProfile(fakePrivateProfileId, "private", 0);
ShadowUserManager.getShadow().addUserProfile(UserHandle.of(fakePrivateProfileId));
// Mock RestrictedLockUtils.checkIfRestrictionEnforced and let it return non-null.
final List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
enforcingUsers.add(new UserManager.EnforcingUser(fakePrivateProfileId,
UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
final ComponentName componentName = new ComponentName("test", "test");
// Ensure that RestrictedLockUtils.checkIfRestrictionEnforced doesn't return null.
ShadowUserManager.getShadow().setUserRestrictionSources(
UserManager.DISALLOW_SHARE_LOCATION,
UserHandle.of(fakePrivateProfileId),
enforcingUsers);
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(componentName);
mController.displayPreference(mScreen);
ArgumentCaptor<ArraySet<UserHandle>> profilesArgumentCaptor =
ArgumentCaptor.forClass(ArraySet.class);
verify(mSettingsInjector).getInjectedSettings(
any(Context.class), profilesArgumentCaptor.capture());
assertThat(profilesArgumentCaptor.getValue())
.doesNotContain(UserHandle.of(fakePrivateProfileId));
}
@Test
public void privateProfileDisallowShareLocationOff_getAllUserLocationServices() {
mSetFlagsRule.enableFlags(
android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES,
android.multiuser.Flags.FLAG_HANDLE_INTERLEAVED_SETTINGS_FOR_PRIVATE_SPACE);
final int fakePrivateProfileId = 123;
ShadowUserManager.getShadow().setProfileIdsWithDisabled(
new int[]{UserHandle.myUserId(), fakePrivateProfileId});
ShadowUserManager.getShadow().addProfile(new UserInfo(UserHandle.myUserId(), "", 0));
ShadowUserManager.getShadow().setPrivateProfile(fakePrivateProfileId, "private", 0);
ShadowUserManager.getShadow().addUserProfile(UserHandle.of(fakePrivateProfileId));
// Mock RestrictedLockUtils.checkIfRestrictionEnforced and let it return null.
// Empty enforcing users.
final List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
ShadowUserManager.getShadow().setUserRestrictionSources(
UserManager.DISALLOW_SHARE_LOCATION,
UserHandle.of(fakePrivateProfileId),
enforcingUsers);
mController.displayPreference(mScreen);
ArgumentCaptor<ArraySet<UserHandle>> profilesArgumentCaptor =
ArgumentCaptor.forClass(ArraySet.class);
verify(mSettingsInjector).getInjectedSettings(
any(Context.class), profilesArgumentCaptor.capture());
assertThat(profilesArgumentCaptor.getValue()).contains(UserHandle.of(fakePrivateProfileId));
}
@Test
@@ -180,7 +262,7 @@ public class LocationInjectedServicesPreferenceControllerTest {
final Map<Integer, List<Preference>> map = new ArrayMap<>();
map.put(UserHandle.myUserId(), preferences);
doReturn(map).when(mSettingsInjector)
.getInjectedSettings(any(Context.class), anyInt());
.getInjectedSettings(any(Context.class), any(ArraySet.class));
ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{UserHandle.myUserId()});
final int userId = UserHandle.myUserId();