1 /*
2  * Copyright (C) 2024 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.car.settings.location;
18 
19 import static android.car.hardware.power.PowerComponent.LOCATION;
20 import static android.location.LocationManager.EXTRA_ADAS_GNSS_ENABLED;
21 import static android.location.LocationManager.EXTRA_LOCATION_ENABLED;
22 
23 import android.car.drivingstate.CarUxRestrictions;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.location.LocationManager;
29 
30 import androidx.annotation.VisibleForTesting;
31 import androidx.preference.Preference;
32 
33 import com.android.car.settings.common.FragmentController;
34 import com.android.car.settings.common.Logger;
35 import com.android.car.settings.common.PowerPolicyListener;
36 import com.android.car.settings.common.PreferenceController;
37 
38 /**
39  * Abstract PreferenceController that listens to location and power policy state
40  * changes and will refresh the UI when events happen.
41  *
42  * @param <V> the upper bound on the type of {@link Preference} on which the controller expects
43  *         to operate.
44  */
45 public abstract class LocationStateListenerBasePreferenceController<V extends Preference> extends
46             PreferenceController<V> {
47     protected static final Logger LOG =
48             new Logger(LocationStateListenerBasePreferenceController.class);
49 
50     /**
51      * A Listener which responds to enabling or disabling of {@link
52      * LocationManager#MODE_CHANGED_ACTION} and {@link
53      * LocationManager#ACTION_ADAS_GNSS_ENABLED_CHANGED} on the device.
54      */
55     public interface LocationStateListener {
56 
57         /**
58          * A callback run any time we receive a broadcast stating the location enable state has
59          * changed.
60          * @param isEnabled Whether or not location is enabled
61          */
onLocationStateChange(boolean isEnabled)62         void onLocationStateChange(boolean isEnabled);
63     }
64 
65     private final LocationManager mLocationManager;
66     private final BroadcastReceiver mLocationReceiver = new BroadcastReceiver() {
67         @Override
68         public void onReceive(Context context, Intent intent) {
69             if (intent.getAction().equals(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED)) {
70                 boolean isBypassLocationEnabled =
71                         intent.getBooleanExtra(EXTRA_ADAS_GNSS_ENABLED, true);
72                 mBypassLocationStateListener.onLocationStateChange(isBypassLocationEnabled);
73             }
74             if (intent.getAction().equals(LocationManager.MODE_CHANGED_ACTION)) {
75                 boolean isMainLocationEnabled =
76                         intent.getBooleanExtra(EXTRA_LOCATION_ENABLED, true);
77                 mMainLocationStateListener.onLocationStateChange(isMainLocationEnabled);
78             }
79             refreshUi();
80         }
81     };
82 
83     private LocationStateListener mBypassLocationStateListener;
84     private LocationStateListener mMainLocationStateListener;
85     @VisibleForTesting
86     PowerPolicyListener mPowerPolicyListener;
87     private boolean mIsPowerPolicyOn = true;
88 
LocationStateListenerBasePreferenceController( Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)89     public LocationStateListenerBasePreferenceController(
90             Context context,
91             String preferenceKey,
92             FragmentController fragmentController,
93             CarUxRestrictions uxRestrictions) {
94         super(context, preferenceKey, fragmentController, uxRestrictions);
95         mLocationManager = context.getSystemService(LocationManager.class);
96     }
97 
98     @Override
onStartInternal()99     protected void onStartInternal() {
100         if (mBypassLocationStateListener == null && mMainLocationStateListener == null) {
101             return;
102         }
103         IntentFilter locationChangeFilter = new IntentFilter();
104         if (mBypassLocationStateListener != null) {
105             locationChangeFilter.addAction(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED);
106         }
107         if (mMainLocationStateListener != null) {
108             locationChangeFilter.addAction(LocationManager.MODE_CHANGED_ACTION);
109         }
110         getContext().registerReceiver(
111                 mLocationReceiver, locationChangeFilter, Context.RECEIVER_NOT_EXPORTED);
112     }
113 
114     @Override
onResumeInternal()115     protected void onResumeInternal() {
116         if (mPowerPolicyListener != null) {
117             mPowerPolicyListener.handleCurrentPolicy();
118         }
119     }
120 
121     @Override
onStopInternal()122     protected void onStopInternal() {
123         if (mBypassLocationStateListener != null || mMainLocationStateListener != null) {
124             getContext().unregisterReceiver(mLocationReceiver);
125         }
126     }
127 
128     @Override
onDestroyInternal()129     protected void onDestroyInternal() {
130         if (mPowerPolicyListener != null) {
131             mPowerPolicyListener.release();
132         }
133     }
134 
setBypassLocationStateListener(LocationStateListener listener)135     private void setBypassLocationStateListener(LocationStateListener listener) {
136         mBypassLocationStateListener = listener;
137     }
138 
139     /**
140      * Add the default bypass location state listener.
141      * The default listener triggers a UI refresh when the state changes.
142      */
addDefaultBypassLocationStateListener()143     protected void addDefaultBypassLocationStateListener() {
144         setBypassLocationStateListener(isEnabled -> {});
145     }
146 
setMainLocationStateListener(LocationStateListener listener)147     protected void setMainLocationStateListener(LocationStateListener listener) {
148         mMainLocationStateListener = listener;
149     }
150 
151     /**
152      * Add the default main location state listener.
153      * The default listener triggers a UI refresh when the state changes.
154      */
addDefaultMainLocationStateListener()155     protected void addDefaultMainLocationStateListener() {
156         setMainLocationStateListener(isEnabled -> {});
157     }
158 
setPowerPolicyListener(PowerPolicyListener listener)159     private void setPowerPolicyListener(PowerPolicyListener listener) {
160         mPowerPolicyListener = listener;
161     }
162 
163     /**
164      * Add the default location power policy listener.
165      * The default listener triggers a UI refresh when the state changes.
166      */
addDefaultPowerPolicyListener()167     protected void addDefaultPowerPolicyListener() {
168         setPowerPolicyListener(new PowerPolicyListener(getContext(), LOCATION,
169                 isOn -> {
170                     mIsPowerPolicyOn = isOn;
171                     refreshUi();
172                 }));
173     }
174 
getLocationManager()175     protected LocationManager getLocationManager() {
176         return mLocationManager;
177     }
178 
getIsPowerPolicyOn()179     protected boolean getIsPowerPolicyOn() {
180         return mIsPowerPolicyOn;
181     }
182 }
183 
184