/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.policy; import static android.app.AppOpsManager.OP_COARSE_LOCATION; import static android.app.AppOpsManager.OP_FINE_LOCATION; import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION; import static com.android.settingslib.Utils.updateLocationEnabled; import android.app.AppOpsManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.location.LocationManager; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; import android.provider.Settings; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.systemui.BootCompleteCache; import com.android.systemui.appops.AppOpItem; import com.android.systemui.appops.AppOpsController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.settings.SecureSettings; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; /** * A controller to manage changes of location related states and update the views accordingly. */ @SysUISingleton public class LocationControllerImpl extends BroadcastReceiver implements LocationController, AppOpsController.Callback { private final Context mContext; private final AppOpsController mAppOpsController; private final DeviceConfigProxy mDeviceConfigProxy; private final BootCompleteCache mBootCompleteCache; private final UserTracker mUserTracker; private final UiEventLogger mUiEventLogger; private final H mHandler; private final Handler mBackgroundHandler; private final PackageManager mPackageManager; private final ContentObserver mContentObserver; private final SecureSettings mSecureSettings; private boolean mAreActiveLocationRequests; private boolean mShouldDisplayAllAccesses; private boolean mShowSystemAccessesFlag; private boolean mShowSystemAccessesSetting; @Inject public LocationControllerImpl(Context context, AppOpsController appOpsController, DeviceConfigProxy deviceConfigProxy, @Main Looper mainLooper, @Background Handler backgroundHandler, BroadcastDispatcher broadcastDispatcher, BootCompleteCache bootCompleteCache, UserTracker userTracker, PackageManager packageManager, UiEventLogger uiEventLogger, SecureSettings secureSettings) { mContext = context; mAppOpsController = appOpsController; mDeviceConfigProxy = deviceConfigProxy; mBootCompleteCache = bootCompleteCache; mHandler = new H(mainLooper); mUserTracker = userTracker; mUiEventLogger = uiEventLogger; mSecureSettings = secureSettings; mBackgroundHandler = backgroundHandler; mPackageManager = packageManager; mShouldDisplayAllAccesses = getAllAccessesSetting(); mShowSystemAccessesFlag = getShowSystemFlag(); mShowSystemAccessesSetting = getShowSystemSetting(); mContentObserver = new ContentObserver(mBackgroundHandler) { @Override public void onChange(boolean selfChange) { mShowSystemAccessesSetting = getShowSystemSetting(); } }; // Register to listen for changes in Settings.Secure settings. mSecureSettings.registerContentObserverForUser( Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, mContentObserver, UserHandle.USER_ALL); // Register to listen for changes in DeviceConfig settings. mDeviceConfigProxy.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_PRIVACY, backgroundHandler::post, properties -> { mShouldDisplayAllAccesses = getAllAccessesSetting(); mShowSystemAccessesFlag = getShowSystemSetting(); updateActiveLocationRequests(); }); // Register to listen for changes in location settings. IntentFilter filter = new IntentFilter(); filter.addAction(LocationManager.MODE_CHANGED_ACTION); broadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler, UserHandle.ALL); // Listen to all accesses and filter the ones interested in based on flags. mAppOpsController.addCallback( new int[]{OP_COARSE_LOCATION, OP_FINE_LOCATION, OP_MONITOR_HIGH_POWER_LOCATION}, this); // Examine the current location state and initialize the status view. backgroundHandler.post(this::updateActiveLocationRequests); } /** * Add a callback to listen for changes in location settings. */ @Override public void addCallback(@NonNull LocationChangeCallback cb) { mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget(); mHandler.sendEmptyMessage(H.MSG_LOCATION_SETTINGS_CHANGED); } @Override public void removeCallback(@NonNull LocationChangeCallback cb) { mHandler.obtainMessage(H.MSG_REMOVE_CALLBACK, cb).sendToTarget(); } /** * Enable or disable location in settings. * *
This will attempt to enable/disable every type of location setting * (e.g. high and balanced power). * *
If enabling, a user consent dialog will pop up prompting the user to accept.
* If the user doesn't accept, network location won't be enabled.
*
* @return true if attempt to change setting was successful.
*/
public boolean setLocationEnabled(boolean enabled) {
// QuickSettings always runs as the owner, so specifically set the settings
// for the current foreground user.
int currentUserId = mUserTracker.getUserId();
if (isUserLocationRestricted(currentUserId)) {
return false;
}
// When enabling location, a user consent dialog will pop up, and the
// setting won't be fully enabled until the user accepts the agreement.
updateLocationEnabled(mContext, enabled, currentUserId,
Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS);
return true;
}
/**
* Returns true if location is enabled in settings. Will return false if
* {@link LocationManager} service has not been completely initialized
*/
public boolean isLocationEnabled() {
// QuickSettings always runs as the owner, so specifically retrieve the settings
// for the current foreground user.
LocationManager locationManager =
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
return mBootCompleteCache.isBootComplete() && locationManager.isLocationEnabledForUser(
mUserTracker.getUserHandle());
}
@Override
public boolean isLocationActive() {
return mAreActiveLocationRequests;
}
/**
* Returns true if the current user is restricted from using location.
*/
private boolean isUserLocationRestricted(int userId) {
final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
return um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION,
UserHandle.of(userId));
}
private boolean getAllAccessesSetting() {
return mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED, false);
}
private boolean getShowSystemFlag() {
return mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SHOW_SYSTEM, false);
}
private boolean getShowSystemSetting() {
return mSecureSettings.getIntForUser(Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 0,
UserHandle.USER_CURRENT) == 1;
}
/**
* Returns true if there currently exist active high power location requests.
*/
@VisibleForTesting
protected boolean areActiveHighPowerLocationRequests() {
List