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