1 /* 2 * Copyright (C) 2015 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 package com.android.permissioncontroller.permission.utils; 17 18 import static android.location.LocationManager.EXTRA_LOCATION_ENABLED; 19 20 import android.Manifest; 21 import android.app.AlertDialog; 22 import android.content.ActivityNotFoundException; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.DialogInterface.OnClickListener; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.location.LocationManager; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.os.UserHandle; 33 import android.provider.Settings; 34 import android.util.Log; 35 36 import androidx.annotation.NonNull; 37 38 import com.android.permissioncontroller.PermissionControllerApplication; 39 import com.android.permissioncontroller.R; 40 41 import java.util.ArrayList; 42 43 public class LocationUtils { 44 45 public static final String LOCATION_PERMISSION = Manifest.permission_group.LOCATION; 46 public static final String ACTIVITY_RECOGNITION_PERMISSION = 47 Manifest.permission_group.ACTIVITY_RECOGNITION; 48 49 private static final String TAG = LocationUtils.class.getSimpleName(); 50 private static final long LOCATION_UPDATE_DELAY_MS = 1000; 51 private static final Handler sMainHandler = new Handler(Looper.getMainLooper()); 52 showLocationDialog(final Context context, CharSequence label)53 public static void showLocationDialog(final Context context, CharSequence label) { 54 new AlertDialog.Builder(context) 55 .setIcon(R.drawable.ic_dialog_alert_material) 56 .setTitle(android.R.string.dialog_alert_title) 57 .setMessage(context.getString(R.string.location_warning, label)) 58 .setNegativeButton(R.string.ok, null) 59 .setPositiveButton(R.string.location_settings, new OnClickListener() { 60 @Override 61 public void onClick(DialogInterface dialog, int which) { 62 context.startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)); 63 } 64 }) 65 .show(); 66 } 67 68 /** Start the settings page for the location controller extra package. */ startLocationControllerExtraPackageSettings(@onNull Context context, @NonNull UserHandle user)69 public static void startLocationControllerExtraPackageSettings(@NonNull Context context, 70 @NonNull UserHandle user) { 71 try { 72 context.startActivityAsUser(new Intent( 73 Settings.ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS), user); 74 } catch (ActivityNotFoundException e) { 75 // In rare cases where location controller extra package is set, but 76 // no activity exists to handle the location controller extra package settings 77 // intent, log an error instead of crashing permission controller. 78 Log.e(TAG, "No activity to handle " 79 + "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS"); 80 } 81 } 82 isLocationEnabled(Context context)83 public static boolean isLocationEnabled(Context context) { 84 return context.getSystemService(LocationManager.class).isLocationEnabled(); 85 } 86 isLocationGroupAndProvider(Context context, String groupName, String packageName)87 public static boolean isLocationGroupAndProvider(Context context, String groupName, 88 String packageName) { 89 return LOCATION_PERMISSION.equals(groupName) 90 && context.getSystemService(LocationManager.class).isProviderPackage(packageName); 91 } 92 isLocationGroupAndControllerExtraPackage(@onNull Context context, @NonNull String groupName, @NonNull String packageName)93 public static boolean isLocationGroupAndControllerExtraPackage(@NonNull Context context, 94 @NonNull String groupName, @NonNull String packageName) { 95 return (LOCATION_PERMISSION.equals(groupName) 96 || ACTIVITY_RECOGNITION_PERMISSION.equals(groupName)) 97 && packageName.equals(context.getSystemService(LocationManager.class) 98 .getExtraLocationControllerPackage()); 99 } 100 101 /** Returns whether the location controller extra package is enabled. */ isExtraLocationControllerPackageEnabled(Context context)102 public static boolean isExtraLocationControllerPackageEnabled(Context context) { 103 try { 104 return context.getSystemService(LocationManager.class) 105 .isExtraLocationControllerPackageEnabled(); 106 } catch (Exception e) { 107 return false; 108 } 109 110 } 111 112 /** 113 * A Listener which responds to enabling or disabling of location on the device 114 */ 115 public interface LocationListener { 116 117 /** 118 * A callback run any time we receive a broadcast stating the location enable state has 119 * changed. 120 * @param enabled Whether or not location is enabled 121 */ onLocationStateChange(boolean enabled)122 void onLocationStateChange(boolean enabled); 123 } 124 125 private static final ArrayList<LocationListener> sLocationListeners = new ArrayList<>(); 126 127 private static BroadcastReceiver sLocationBroadcastReceiver = new BroadcastReceiver() { 128 @Override 129 public void onReceive(Context context, Intent intent) { 130 boolean isEnabled = intent.getBooleanExtra(EXTRA_LOCATION_ENABLED, true); 131 sMainHandler.postDelayed(() -> { 132 synchronized (sLocationListeners) { 133 for (LocationListener l : sLocationListeners) { 134 l.onLocationStateChange(isEnabled); 135 } 136 } 137 }, LOCATION_UPDATE_DELAY_MS); 138 } 139 }; 140 141 /** 142 * Add a LocationListener, which will be notified if the location provider is enabled or 143 * disabled 144 * @param listener the listener to add 145 */ addLocationListener(LocationListener listener)146 public static void addLocationListener(LocationListener listener) { 147 synchronized (sLocationListeners) { 148 boolean wasEmpty = sLocationListeners.isEmpty(); 149 sLocationListeners.add(listener); 150 if (wasEmpty) { 151 IntentFilter intentFilter = new IntentFilter(LocationManager.MODE_CHANGED_ACTION); 152 PermissionControllerApplication.get().getApplicationContext() 153 .registerReceiverForAllUsers(sLocationBroadcastReceiver, intentFilter, 154 null, null); 155 } 156 } 157 } 158 159 /** 160 * Remove a LocationListener 161 * @param listener The listener to remove 162 * 163 * @return True if it was successfully removed, false otherwise 164 */ removeLocationListener(LocationListener listener)165 public static boolean removeLocationListener(LocationListener listener) { 166 synchronized (sLocationListeners) { 167 boolean success = sLocationListeners.remove(listener); 168 if (success && sLocationListeners.isEmpty()) { 169 PermissionControllerApplication.get().getApplicationContext() 170 .unregisterReceiver(sLocationBroadcastReceiver); 171 } 172 return success; 173 } 174 } 175 } 176