LocationController.java revision b12ba933f3db9280edcb6a3591741d29c109a4e2
1/* 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 19import android.app.ActivityManager; 20import android.app.AppOpsManager; 21import android.app.StatusBarManager; 22import android.content.BroadcastReceiver; 23import android.content.ContentResolver; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.database.ContentObserver; 28import android.location.LocationManager; 29import android.os.Handler; 30import android.os.UserHandle; 31import android.os.UserManager; 32import android.provider.Settings; 33 34import com.android.systemui.R; 35 36import java.util.ArrayList; 37import java.util.List; 38 39/** 40 * A controller to manage changes of location related states and update the views accordingly. 41 */ 42public class LocationController extends BroadcastReceiver { 43 // The name of the placeholder corresponding to the location request status icon. 44 // This string corresponds to config_statusBarIcons in core/res/res/values/config.xml. 45 private static final String LOCATION_STATUS_ICON_PLACEHOLDER = "location"; 46 private static final int LOCATION_STATUS_ICON_ID 47 = R.drawable.stat_sys_device_access_location_found; 48 49 private static final int[] mHighPowerRequestAppOpArray 50 = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION}; 51 52 private Context mContext; 53 54 private AppOpsManager mAppOpsManager; 55 private StatusBarManager mStatusBarManager; 56 57 private boolean mAreActiveLocationRequests; 58 59 private ArrayList<LocationSettingsChangeCallback> mSettingsChangeCallbacks = 60 new ArrayList<LocationSettingsChangeCallback>(); 61 62 /** 63 * A callback for change in location settings (the user has enabled/disabled location). 64 */ 65 public interface LocationSettingsChangeCallback { 66 /** 67 * Called whenever location settings change. 68 * 69 * @param locationEnabled A value of true indicates that at least one type of location 70 * is enabled in settings. 71 */ 72 public void onLocationSettingsChanged(boolean locationEnabled); 73 } 74 75 public LocationController(Context context) { 76 mContext = context; 77 78 IntentFilter filter = new IntentFilter(); 79 filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); 80 context.registerReceiver(this, filter); 81 82 mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 83 mStatusBarManager 84 = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); 85 86 // Register to listen for changes in location settings. 87 IntentFilter intentFilter = new IntentFilter(); 88 intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION); 89 context.registerReceiverAsUser(new BroadcastReceiver() { 90 @Override 91 public void onReceive(Context context, Intent intent) { 92 String action = intent.getAction(); 93 if (LocationManager.MODE_CHANGED_ACTION.equals(action)) { 94 locationSettingsChanged(); 95 } 96 } 97 }, UserHandle.ALL, intentFilter, null, new Handler()); 98 99 // Examine the current location state and initialize the status view. 100 updateActiveLocationRequests(); 101 refreshViews(); 102 } 103 104 /** 105 * Add a callback to listen for changes in location settings. 106 */ 107 public void addSettingsChangedCallback(LocationSettingsChangeCallback cb) { 108 mSettingsChangeCallbacks.add(cb); 109 } 110 111 /** 112 * Enable or disable location in settings. 113 * 114 * <p>This will attempt to enable/disable every type of location setting 115 * (e.g. high and balanced power). 116 * 117 * <p>If enabling, a user consent dialog will pop up prompting the user to accept. 118 * If the user doesn't accept, network location won't be enabled. 119 * 120 * @return true if attempt to change setting was successful. 121 */ 122 public boolean setLocationEnabled(boolean enabled) { 123 int currentUserId = ActivityManager.getCurrentUser(); 124 if (isUserLocationRestricted(currentUserId)) { 125 return false; 126 } 127 final ContentResolver cr = mContext.getContentResolver(); 128 // When enabling location, a user consent dialog will pop up, and the 129 // setting won't be fully enabled until the user accepts the agreement. 130 int mode = enabled 131 ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY : Settings.Secure.LOCATION_MODE_OFF; 132 return Settings.Secure 133 .putIntForUser(cr, Settings.Secure.LOCATION_MODE, mode, currentUserId); 134 } 135 136 /** 137 * Returns true if location isn't disabled in settings. 138 */ 139 public boolean isLocationEnabled() { 140 int currentUserId = ActivityManager.getCurrentUser(); 141 if (isUserLocationRestricted(currentUserId)) { 142 return false; 143 } 144 145 ContentResolver resolver = mContext.getContentResolver(); 146 int mode = Settings.Secure.getIntForUser(resolver, Settings.Secure.LOCATION_MODE, 147 Settings.Secure.LOCATION_MODE_OFF, currentUserId); 148 return mode != Settings.Secure.LOCATION_MODE_OFF; 149 } 150 151 /** 152 * Returns true if the current user is restricted from using location. 153 */ 154 private boolean isUserLocationRestricted(int userId) { 155 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 156 return um.hasUserRestriction( 157 UserManager.DISALLOW_SHARE_LOCATION, 158 new UserHandle(userId)); 159 } 160 161 /** 162 * Returns true if there currently exist active high power location requests. 163 */ 164 private boolean areActiveHighPowerLocationRequests() { 165 List<AppOpsManager.PackageOps> packages 166 = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray); 167 // AppOpsManager can return null when there is no requested data. 168 if (packages != null) { 169 final int numPackages = packages.size(); 170 for (int packageInd = 0; packageInd < numPackages; packageInd++) { 171 AppOpsManager.PackageOps packageOp = packages.get(packageInd); 172 List<AppOpsManager.OpEntry> opEntries = packageOp.getOps(); 173 if (opEntries != null) { 174 final int numOps = opEntries.size(); 175 for (int opInd = 0; opInd < numOps; opInd++) { 176 AppOpsManager.OpEntry opEntry = opEntries.get(opInd); 177 // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because 178 // of the mHighPowerRequestAppOpArray filter, but checking defensively. 179 if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { 180 if (opEntry.isRunning()) { 181 return true; 182 } 183 } 184 } 185 } 186 } 187 } 188 189 return false; 190 } 191 192 // Updates the status view based on the current state of location requests. 193 private void refreshViews() { 194 if (mAreActiveLocationRequests) { 195 mStatusBarManager.setIcon(LOCATION_STATUS_ICON_PLACEHOLDER, LOCATION_STATUS_ICON_ID, 0, 196 mContext.getString(R.string.accessibility_location_active)); 197 } else { 198 mStatusBarManager.removeIcon(LOCATION_STATUS_ICON_PLACEHOLDER); 199 } 200 } 201 202 // Reads the active location requests and updates the status view if necessary. 203 private void updateActiveLocationRequests() { 204 boolean hadActiveLocationRequests = mAreActiveLocationRequests; 205 mAreActiveLocationRequests = areActiveHighPowerLocationRequests(); 206 if (mAreActiveLocationRequests != hadActiveLocationRequests) { 207 refreshViews(); 208 } 209 } 210 211 private void locationSettingsChanged() { 212 boolean isEnabled = isLocationEnabled(); 213 for (LocationSettingsChangeCallback cb : mSettingsChangeCallbacks) { 214 cb.onLocationSettingsChanged(isEnabled); 215 } 216 } 217 218 @Override 219 public void onReceive(Context context, Intent intent) { 220 final String action = intent.getAction(); 221 if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) { 222 updateActiveLocationRequests(); 223 } 224 } 225} 226