blob: 5a97eedd9b2cbff1005cef16bc8ab1a5e0b5f645 [file] [log] [blame]
John Spurlockaf8d6c42014-05-07 17:49:08 -04001/*
2 * Copyright (C) 2008 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
17package com.android.systemui.statusbar.policy;
18
Maggieaa080f92018-01-04 15:35:11 -080019import static com.android.settingslib.Utils.updateLocationEnabled;
20
John Spurlockaf8d6c42014-05-07 17:49:08 -040021import android.app.ActivityManager;
22import android.app.AppOpsManager;
23import android.app.StatusBarManager;
24import android.content.BroadcastReceiver;
John Spurlockaf8d6c42014-05-07 17:49:08 -040025import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.location.LocationManager;
29import android.os.Handler;
Jason Monk05b86bc2015-05-19 14:21:28 -040030import android.os.Looper;
31import android.os.Message;
John Spurlockaf8d6c42014-05-07 17:49:08 -040032import android.os.UserHandle;
33import android.os.UserManager;
Lifu Tang0cba58f2018-01-23 21:14:15 -080034import android.provider.Settings;
Gus Prevasab336792018-11-14 13:52:20 -050035
Aurimas Liutikasfd52c142018-04-17 09:50:46 -070036import androidx.annotation.VisibleForTesting;
Gus Prevasab336792018-11-14 13:52:20 -050037
Dave Mankofff4736812019-10-18 17:25:50 -040038import com.android.systemui.dagger.qualifiers.BgLooper;
Jason Monkb46a3c92017-06-22 09:19:54 -040039import com.android.systemui.util.Utils;
Gus Prevasab336792018-11-14 13:52:20 -050040
John Spurlockaf8d6c42014-05-07 17:49:08 -040041import java.util.ArrayList;
42import java.util.List;
43
Jason Monk196d6392018-12-20 13:25:34 -050044import javax.inject.Inject;
Jason Monk196d6392018-12-20 13:25:34 -050045import javax.inject.Singleton;
46
John Spurlockaf8d6c42014-05-07 17:49:08 -040047/**
48 * A controller to manage changes of location related states and update the views accordingly.
49 */
Jason Monk196d6392018-12-20 13:25:34 -050050@Singleton
John Spurlockaf8d6c42014-05-07 17:49:08 -040051public class LocationControllerImpl extends BroadcastReceiver implements LocationController {
John Spurlockaf8d6c42014-05-07 17:49:08 -040052
53 private static final int[] mHighPowerRequestAppOpArray
54 = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION};
55
56 private Context mContext;
57
58 private AppOpsManager mAppOpsManager;
59 private StatusBarManager mStatusBarManager;
60
61 private boolean mAreActiveLocationRequests;
62
Jason Monk359bb742017-04-13 10:40:40 -040063 private ArrayList<LocationChangeCallback> mSettingsChangeCallbacks =
64 new ArrayList<LocationChangeCallback>();
Jason Monk05b86bc2015-05-19 14:21:28 -040065 private final H mHandler = new H();
John Spurlockaf8d6c42014-05-07 17:49:08 -040066
Jason Monk196d6392018-12-20 13:25:34 -050067 @Inject
Dave Mankofff4736812019-10-18 17:25:50 -040068 public LocationControllerImpl(Context context, @BgLooper Looper bgLooper) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040069 mContext = context;
70
Jason Monk05b86bc2015-05-19 14:21:28 -040071 // Register to listen for changes in location settings.
John Spurlockaf8d6c42014-05-07 17:49:08 -040072 IntentFilter filter = new IntentFilter();
73 filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
Jason Monk05b86bc2015-05-19 14:21:28 -040074 filter.addAction(LocationManager.MODE_CHANGED_ACTION);
75 context.registerReceiverAsUser(this, UserHandle.ALL, filter, null, new Handler(bgLooper));
John Spurlockaf8d6c42014-05-07 17:49:08 -040076
77 mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
78 mStatusBarManager
79 = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
80
John Spurlockaf8d6c42014-05-07 17:49:08 -040081 // Examine the current location state and initialize the status view.
82 updateActiveLocationRequests();
John Spurlockaf8d6c42014-05-07 17:49:08 -040083 }
84
85 /**
86 * Add a callback to listen for changes in location settings.
87 */
Jason Monk359bb742017-04-13 10:40:40 -040088 public void addCallback(LocationChangeCallback cb) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040089 mSettingsChangeCallbacks.add(cb);
Jason Monk05b86bc2015-05-19 14:21:28 -040090 mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED);
John Spurlockaf8d6c42014-05-07 17:49:08 -040091 }
92
Jason Monk359bb742017-04-13 10:40:40 -040093 public void removeCallback(LocationChangeCallback cb) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040094 mSettingsChangeCallbacks.remove(cb);
95 }
96
97 /**
98 * Enable or disable location in settings.
99 *
100 * <p>This will attempt to enable/disable every type of location setting
101 * (e.g. high and balanced power).
102 *
103 * <p>If enabling, a user consent dialog will pop up prompting the user to accept.
104 * If the user doesn't accept, network location won't be enabled.
105 *
106 * @return true if attempt to change setting was successful.
107 */
108 public boolean setLocationEnabled(boolean enabled) {
Maggieaa080f92018-01-04 15:35:11 -0800109 // QuickSettings always runs as the owner, so specifically set the settings
110 // for the current foreground user.
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000111 int currentUserId = ActivityManager.getCurrentUser();
112 if (isUserLocationRestricted(currentUserId)) {
113 return false;
114 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400115 // When enabling location, a user consent dialog will pop up, and the
116 // setting won't be fully enabled until the user accepts the agreement.
Lifu Tang0cba58f2018-01-23 21:14:15 -0800117 updateLocationEnabled(mContext, enabled, currentUserId,
118 Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
Maggieaa080f92018-01-04 15:35:11 -0800119 return true;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400120 }
121
122 /**
Maggieaa080f92018-01-04 15:35:11 -0800123 * Returns true if location is enabled in settings.
John Spurlockaf8d6c42014-05-07 17:49:08 -0400124 */
125 public boolean isLocationEnabled() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400126 // QuickSettings always runs as the owner, so specifically retrieve the settings
127 // for the current foreground user.
Maggieaa080f92018-01-04 15:35:11 -0800128 LocationManager locationManager =
129 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
Maggie91e630c2018-01-24 17:31:46 -0800130 return locationManager.isLocationEnabledForUser(
131 UserHandle.of(ActivityManager.getCurrentUser()));
John Spurlockaf8d6c42014-05-07 17:49:08 -0400132 }
133
Jason Monk359bb742017-04-13 10:40:40 -0400134 @Override
135 public boolean isLocationActive() {
136 return mAreActiveLocationRequests;
137 }
138
John Spurlockaf8d6c42014-05-07 17:49:08 -0400139 /**
140 * Returns true if the current user is restricted from using location.
141 */
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000142 private boolean isUserLocationRestricted(int userId) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400143 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
Sudheer Shankab6fc9312016-01-27 19:59:03 +0000144 return um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION,
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000145 UserHandle.of(userId));
John Spurlockaf8d6c42014-05-07 17:49:08 -0400146 }
147
148 /**
149 * Returns true if there currently exist active high power location requests.
150 */
Jason Monkb46a3c92017-06-22 09:19:54 -0400151 @VisibleForTesting
152 protected boolean areActiveHighPowerLocationRequests() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400153 List<AppOpsManager.PackageOps> packages
154 = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray);
155 // AppOpsManager can return null when there is no requested data.
156 if (packages != null) {
157 final int numPackages = packages.size();
158 for (int packageInd = 0; packageInd < numPackages; packageInd++) {
159 AppOpsManager.PackageOps packageOp = packages.get(packageInd);
160 List<AppOpsManager.OpEntry> opEntries = packageOp.getOps();
161 if (opEntries != null) {
162 final int numOps = opEntries.size();
163 for (int opInd = 0; opInd < numOps; opInd++) {
164 AppOpsManager.OpEntry opEntry = opEntries.get(opInd);
165 // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because
166 // of the mHighPowerRequestAppOpArray filter, but checking defensively.
167 if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
168 if (opEntry.isRunning()) {
169 return true;
170 }
171 }
172 }
173 }
174 }
175 }
176
177 return false;
178 }
179
John Spurlockaf8d6c42014-05-07 17:49:08 -0400180 // Reads the active location requests and updates the status view if necessary.
181 private void updateActiveLocationRequests() {
182 boolean hadActiveLocationRequests = mAreActiveLocationRequests;
183 mAreActiveLocationRequests = areActiveHighPowerLocationRequests();
184 if (mAreActiveLocationRequests != hadActiveLocationRequests) {
Jason Monk359bb742017-04-13 10:40:40 -0400185 mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400186 }
187 }
188
John Spurlockaf8d6c42014-05-07 17:49:08 -0400189 @Override
190 public void onReceive(Context context, Intent intent) {
191 final String action = intent.getAction();
192 if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) {
193 updateActiveLocationRequests();
Jason Monk05b86bc2015-05-19 14:21:28 -0400194 } else if (LocationManager.MODE_CHANGED_ACTION.equals(action)) {
195 mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED);
196 }
197 }
198
199 private final class H extends Handler {
200 private static final int MSG_LOCATION_SETTINGS_CHANGED = 1;
Jason Monk359bb742017-04-13 10:40:40 -0400201 private static final int MSG_LOCATION_ACTIVE_CHANGED = 2;
Jason Monk05b86bc2015-05-19 14:21:28 -0400202
203 @Override
204 public void handleMessage(Message msg) {
205 switch (msg.what) {
206 case MSG_LOCATION_SETTINGS_CHANGED:
207 locationSettingsChanged();
208 break;
Jason Monk359bb742017-04-13 10:40:40 -0400209 case MSG_LOCATION_ACTIVE_CHANGED:
210 locationActiveChanged();
211 break;
212 }
213 }
214
215 private void locationActiveChanged() {
Jason Monkb46a3c92017-06-22 09:19:54 -0400216 Utils.safeForeach(mSettingsChangeCallbacks,
217 cb -> cb.onLocationActiveChanged(mAreActiveLocationRequests));
Jason Monk05b86bc2015-05-19 14:21:28 -0400218 }
219
220 private void locationSettingsChanged() {
221 boolean isEnabled = isLocationEnabled();
Jason Monkb46a3c92017-06-22 09:19:54 -0400222 Utils.safeForeach(mSettingsChangeCallbacks,
223 cb -> cb.onLocationSettingsChanged(isEnabled));
John Spurlockaf8d6c42014-05-07 17:49:08 -0400224 }
225 }
226}