blob: 0b666a6106e19dc5ca2a7fa66f754244b3dc22ef [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;
Maggieaa080f92018-01-04 15:35:11 -080032import android.os.Process;
John Spurlockaf8d6c42014-05-07 17:49:08 -040033import android.os.UserHandle;
34import android.os.UserManager;
Jason Monkb46a3c92017-06-22 09:19:54 -040035import android.support.annotation.VisibleForTesting;
Jason Monkb46a3c92017-06-22 09:19:54 -040036import com.android.systemui.util.Utils;
John Spurlockaf8d6c42014-05-07 17:49:08 -040037import java.util.ArrayList;
38import java.util.List;
39
40/**
41 * A controller to manage changes of location related states and update the views accordingly.
42 */
43public class LocationControllerImpl extends BroadcastReceiver implements LocationController {
John Spurlockaf8d6c42014-05-07 17:49:08 -040044
45 private static final int[] mHighPowerRequestAppOpArray
46 = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION};
47
48 private Context mContext;
49
50 private AppOpsManager mAppOpsManager;
51 private StatusBarManager mStatusBarManager;
52
53 private boolean mAreActiveLocationRequests;
54
Jason Monk359bb742017-04-13 10:40:40 -040055 private ArrayList<LocationChangeCallback> mSettingsChangeCallbacks =
56 new ArrayList<LocationChangeCallback>();
Jason Monk05b86bc2015-05-19 14:21:28 -040057 private final H mHandler = new H();
John Spurlockaf8d6c42014-05-07 17:49:08 -040058
Jason Monk05b86bc2015-05-19 14:21:28 -040059 public LocationControllerImpl(Context context, Looper bgLooper) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040060 mContext = context;
61
Jason Monk05b86bc2015-05-19 14:21:28 -040062 // Register to listen for changes in location settings.
John Spurlockaf8d6c42014-05-07 17:49:08 -040063 IntentFilter filter = new IntentFilter();
64 filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
Jason Monk05b86bc2015-05-19 14:21:28 -040065 filter.addAction(LocationManager.MODE_CHANGED_ACTION);
66 context.registerReceiverAsUser(this, UserHandle.ALL, filter, null, new Handler(bgLooper));
John Spurlockaf8d6c42014-05-07 17:49:08 -040067
68 mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
69 mStatusBarManager
70 = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
71
John Spurlockaf8d6c42014-05-07 17:49:08 -040072 // Examine the current location state and initialize the status view.
73 updateActiveLocationRequests();
John Spurlockaf8d6c42014-05-07 17:49:08 -040074 }
75
76 /**
77 * Add a callback to listen for changes in location settings.
78 */
Jason Monk359bb742017-04-13 10:40:40 -040079 public void addCallback(LocationChangeCallback cb) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040080 mSettingsChangeCallbacks.add(cb);
Jason Monk05b86bc2015-05-19 14:21:28 -040081 mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED);
John Spurlockaf8d6c42014-05-07 17:49:08 -040082 }
83
Jason Monk359bb742017-04-13 10:40:40 -040084 public void removeCallback(LocationChangeCallback cb) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040085 mSettingsChangeCallbacks.remove(cb);
86 }
87
88 /**
89 * Enable or disable location in settings.
90 *
91 * <p>This will attempt to enable/disable every type of location setting
92 * (e.g. high and balanced power).
93 *
94 * <p>If enabling, a user consent dialog will pop up prompting the user to accept.
95 * If the user doesn't accept, network location won't be enabled.
96 *
97 * @return true if attempt to change setting was successful.
98 */
99 public boolean setLocationEnabled(boolean enabled) {
Maggieaa080f92018-01-04 15:35:11 -0800100 // QuickSettings always runs as the owner, so specifically set the settings
101 // for the current foreground user.
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000102 int currentUserId = ActivityManager.getCurrentUser();
103 if (isUserLocationRestricted(currentUserId)) {
104 return false;
105 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400106 // When enabling location, a user consent dialog will pop up, and the
107 // setting won't be fully enabled until the user accepts the agreement.
Maggieaa080f92018-01-04 15:35:11 -0800108 updateLocationEnabled(mContext, enabled, currentUserId);
109 return true;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400110 }
111
112 /**
Maggieaa080f92018-01-04 15:35:11 -0800113 * Returns true if location is enabled in settings.
John Spurlockaf8d6c42014-05-07 17:49:08 -0400114 */
115 public boolean isLocationEnabled() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400116 // QuickSettings always runs as the owner, so specifically retrieve the settings
117 // for the current foreground user.
Maggieaa080f92018-01-04 15:35:11 -0800118 LocationManager locationManager =
119 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
120 return locationManager.isLocationEnabledForUser(Process.myUserHandle());
John Spurlockaf8d6c42014-05-07 17:49:08 -0400121 }
122
Jason Monk359bb742017-04-13 10:40:40 -0400123 @Override
124 public boolean isLocationActive() {
125 return mAreActiveLocationRequests;
126 }
127
John Spurlockaf8d6c42014-05-07 17:49:08 -0400128 /**
129 * Returns true if the current user is restricted from using location.
130 */
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000131 private boolean isUserLocationRestricted(int userId) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400132 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
Sudheer Shankab6fc9312016-01-27 19:59:03 +0000133 return um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION,
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000134 UserHandle.of(userId));
John Spurlockaf8d6c42014-05-07 17:49:08 -0400135 }
136
137 /**
138 * Returns true if there currently exist active high power location requests.
139 */
Jason Monkb46a3c92017-06-22 09:19:54 -0400140 @VisibleForTesting
141 protected boolean areActiveHighPowerLocationRequests() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400142 List<AppOpsManager.PackageOps> packages
143 = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray);
144 // AppOpsManager can return null when there is no requested data.
145 if (packages != null) {
146 final int numPackages = packages.size();
147 for (int packageInd = 0; packageInd < numPackages; packageInd++) {
148 AppOpsManager.PackageOps packageOp = packages.get(packageInd);
149 List<AppOpsManager.OpEntry> opEntries = packageOp.getOps();
150 if (opEntries != null) {
151 final int numOps = opEntries.size();
152 for (int opInd = 0; opInd < numOps; opInd++) {
153 AppOpsManager.OpEntry opEntry = opEntries.get(opInd);
154 // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because
155 // of the mHighPowerRequestAppOpArray filter, but checking defensively.
156 if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
157 if (opEntry.isRunning()) {
158 return true;
159 }
160 }
161 }
162 }
163 }
164 }
165
166 return false;
167 }
168
John Spurlockaf8d6c42014-05-07 17:49:08 -0400169 // Reads the active location requests and updates the status view if necessary.
170 private void updateActiveLocationRequests() {
171 boolean hadActiveLocationRequests = mAreActiveLocationRequests;
172 mAreActiveLocationRequests = areActiveHighPowerLocationRequests();
173 if (mAreActiveLocationRequests != hadActiveLocationRequests) {
Jason Monk359bb742017-04-13 10:40:40 -0400174 mHandler.sendEmptyMessage(H.MSG_LOCATION_ACTIVE_CHANGED);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400175 }
176 }
177
John Spurlockaf8d6c42014-05-07 17:49:08 -0400178 @Override
179 public void onReceive(Context context, Intent intent) {
180 final String action = intent.getAction();
181 if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) {
182 updateActiveLocationRequests();
Jason Monk05b86bc2015-05-19 14:21:28 -0400183 } else if (LocationManager.MODE_CHANGED_ACTION.equals(action)) {
184 mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED);
185 }
186 }
187
188 private final class H extends Handler {
189 private static final int MSG_LOCATION_SETTINGS_CHANGED = 1;
Jason Monk359bb742017-04-13 10:40:40 -0400190 private static final int MSG_LOCATION_ACTIVE_CHANGED = 2;
Jason Monk05b86bc2015-05-19 14:21:28 -0400191
192 @Override
193 public void handleMessage(Message msg) {
194 switch (msg.what) {
195 case MSG_LOCATION_SETTINGS_CHANGED:
196 locationSettingsChanged();
197 break;
Jason Monk359bb742017-04-13 10:40:40 -0400198 case MSG_LOCATION_ACTIVE_CHANGED:
199 locationActiveChanged();
200 break;
201 }
202 }
203
204 private void locationActiveChanged() {
Jason Monkb46a3c92017-06-22 09:19:54 -0400205 Utils.safeForeach(mSettingsChangeCallbacks,
206 cb -> cb.onLocationActiveChanged(mAreActiveLocationRequests));
Jason Monk05b86bc2015-05-19 14:21:28 -0400207 }
208
209 private void locationSettingsChanged() {
210 boolean isEnabled = isLocationEnabled();
Jason Monkb46a3c92017-06-22 09:19:54 -0400211 Utils.safeForeach(mSettingsChangeCallbacks,
212 cb -> cb.onLocationSettingsChanged(isEnabled));
John Spurlockaf8d6c42014-05-07 17:49:08 -0400213 }
214 }
215}