1 /*
2  * Copyright (C) 2021 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.location;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.location.SettingInjectorService;
24 import android.os.UserHandle;
25 import android.util.ArraySet;
26 import android.util.Log;
27 
28 import androidx.annotation.VisibleForTesting;
29 import androidx.lifecycle.Lifecycle;
30 import androidx.lifecycle.OnLifecycleEvent;
31 import androidx.preference.Preference;
32 import androidx.preference.PreferenceScreen;
33 
34 import com.android.settings.Utils;
35 import com.android.settings.dashboard.DashboardFragment;
36 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
37 import com.android.settingslib.core.lifecycle.LifecycleObserver;
38 
39 import java.util.List;
40 import java.util.Map;
41 
42 /**
43  * A abstract class which is responsible for creating the injected location services items.
44  * Developer needs to implement the {@link #injectLocationServices(PreferenceScreen)}.
45  */
46 public abstract class LocationInjectedServiceBasePreferenceController
47         extends LocationBasePreferenceController implements LifecycleObserver{
48 
49     private static final String TAG = "LocationPrefCtrl";
50     @VisibleForTesting
51     static final IntentFilter INTENT_FILTER_INJECTED_SETTING_CHANGED =
52             new IntentFilter(SettingInjectorService.ACTION_INJECTED_SETTING_CHANGED);
53 
54     @VisibleForTesting
55     AppSettingsInjector mInjector;
56     /** Receives UPDATE_INTENT */
57     @VisibleForTesting
58     BroadcastReceiver mInjectedSettingsReceiver;
59 
LocationInjectedServiceBasePreferenceController(Context context, String key)60     public LocationInjectedServiceBasePreferenceController(Context context, String key) {
61         super(context, key);
62     }
63 
64     @Override
init(DashboardFragment fragment)65     public void init(DashboardFragment fragment) {
66         super.init(fragment);
67         mInjector = new AppSettingsInjector(mContext, getMetricsCategory());
68     }
69 
70     @Override
displayPreference(PreferenceScreen screen)71     public void displayPreference(PreferenceScreen screen) {
72         super.displayPreference(screen);
73         injectLocationServices(screen);
74     }
75 
76     /**
77      * Override this method to inject location services in preference screen.
78      * @param screen
79      */
injectLocationServices(PreferenceScreen screen)80     protected abstract void injectLocationServices(PreferenceScreen screen);
81 
82     @Override
onLocationModeChanged(int mode, boolean restricted)83     public void onLocationModeChanged(int mode, boolean restricted) {
84         // As a safety measure, also reloads on location mode change to ensure the settings are
85         // up-to-date even if an affected app doesn't send the setting changed broadcast.
86         mInjector.reloadStatusMessages();
87     }
88 
89     /** @OnLifecycleEvent(ON_RESUME) */
90     @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
onResume()91     public void onResume() {
92         if (mInjectedSettingsReceiver == null) {
93             mInjectedSettingsReceiver = new BroadcastReceiver() {
94                 @Override
95                 public void onReceive(Context context, Intent intent) {
96                     if (Log.isLoggable(TAG, Log.DEBUG)) {
97                         Log.d(TAG, "Received settings change intent: " + intent);
98                     }
99                     mInjector.reloadStatusMessages();
100                 }
101             };
102         }
103         mContext.registerReceiver(
104                 mInjectedSettingsReceiver, INTENT_FILTER_INJECTED_SETTING_CHANGED,
105                 Context.RECEIVER_EXPORTED_UNAUDITED);
106     }
107 
108     /** @OnLifecycleEvent(ON_PAUSE) */
109     @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
onPause()110     public void onPause() {
111         mContext.unregisterReceiver(mInjectedSettingsReceiver);
112     }
113 
getLocationServices()114     protected Map<Integer, List<Preference>> getLocationServices() {
115         ArraySet<UserHandle> userHandles = new ArraySet<>();
116         userHandles.add(UserHandle.of(UserHandle.myUserId()));
117 
118         // If location access is locked down by device policy then we only show injected settings
119         // for the primary profile.
120         final int managedProfileId = Utils.getManagedProfileId(mUserManager, UserHandle.myUserId());
121         if (managedProfileId != UserHandle.USER_NULL
122                 && mLocationEnabler.getShareLocationEnforcedAdmin(managedProfileId) == null) {
123             userHandles.add(UserHandle.of(managedProfileId));
124         }
125         if (android.os.Flags.allowPrivateProfile()
126                 && android.multiuser.Flags.enablePrivateSpaceFeatures()
127                 && android.multiuser.Flags.handleInterleavedSettingsForPrivateSpace()) {
128             final UserHandle privateProfile = Utils.getProfileOfType(mUserManager,
129                     ProfileSelectFragment.ProfileType.PRIVATE);
130             if (privateProfile != null && mLocationEnabler
131                     .getShareLocationEnforcedAdmin(privateProfile.getIdentifier()) == null) {
132                 userHandles.add(privateProfile);
133             }
134         }
135 
136         return mInjector.getInjectedSettings(mFragment.getPreferenceManager().getContext(),
137                 userHandles);
138     }
139 }
140