1 /* 2 * Copyright (C) 2021 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.server.location.settings; 18 19 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE; 20 21 import android.app.ActivityManager; 22 import android.content.Context; 23 import android.os.Environment; 24 import android.os.RemoteException; 25 import android.util.IndentingPrintWriter; 26 import android.util.SparseArray; 27 28 import com.android.internal.annotations.GuardedBy; 29 import com.android.internal.annotations.VisibleForTesting; 30 import com.android.server.FgThread; 31 32 import java.io.DataInput; 33 import java.io.DataOutput; 34 import java.io.File; 35 import java.io.FileDescriptor; 36 import java.io.IOException; 37 import java.util.concurrent.CopyOnWriteArrayList; 38 import java.util.function.Function; 39 40 /** 41 * Accessor for location user settings. Ensure there is only ever one instance as multiple instances 42 * don't play nicely with each other. 43 */ 44 public class LocationSettings { 45 46 /** Listens for changes to location user settings. */ 47 public interface LocationUserSettingsListener { 48 /** Invoked when location user settings have changed for the given user. */ onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings, LocationUserSettings newSettings)49 void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings, 50 LocationUserSettings newSettings); 51 } 52 53 private static final String LOCATION_DIRNAME = "location"; 54 private static final String LOCATION_SETTINGS_FILENAME = "settings"; 55 56 final Context mContext; 57 58 @GuardedBy("mUserSettings") 59 private final SparseArray<LocationUserSettingsStore> mUserSettings; 60 private final CopyOnWriteArrayList<LocationUserSettingsListener> mUserSettingsListeners; 61 LocationSettings(Context context)62 public LocationSettings(Context context) { 63 mContext = context; 64 mUserSettings = new SparseArray<>(1); 65 mUserSettingsListeners = new CopyOnWriteArrayList<>(); 66 } 67 68 /** Registers a listener for changes to location user settings. */ registerLocationUserSettingsListener(LocationUserSettingsListener listener)69 public final void registerLocationUserSettingsListener(LocationUserSettingsListener listener) { 70 mUserSettingsListeners.add(listener); 71 } 72 73 /** Unregisters a listener for changes to location user settings. */ unregisterLocationUserSettingsListener( LocationUserSettingsListener listener)74 public final void unregisterLocationUserSettingsListener( 75 LocationUserSettingsListener listener) { 76 mUserSettingsListeners.remove(listener); 77 } 78 getUserSettingsDir(int userId)79 protected File getUserSettingsDir(int userId) { 80 return Environment.getDataSystemDeDirectory(userId); 81 } 82 createUserSettingsStore(int userId, File file)83 protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) { 84 return new LocationUserSettingsStore(userId, file); 85 } 86 getUserSettingsStore(int userId)87 private LocationUserSettingsStore getUserSettingsStore(int userId) { 88 synchronized (mUserSettings) { 89 LocationUserSettingsStore settingsStore = mUserSettings.get(userId); 90 if (settingsStore == null) { 91 File file = new File(new File(getUserSettingsDir(userId), LOCATION_DIRNAME), 92 LOCATION_SETTINGS_FILENAME); 93 settingsStore = createUserSettingsStore(userId, file); 94 mUserSettings.put(userId, settingsStore); 95 } 96 return settingsStore; 97 } 98 } 99 100 /** Retrieves the current state of location user settings. */ getUserSettings(int userId)101 public final LocationUserSettings getUserSettings(int userId) { 102 return getUserSettingsStore(userId).get(); 103 } 104 105 /** Updates the current state of location user settings for the given user. */ updateUserSettings(int userId, Function<LocationUserSettings, LocationUserSettings> updater)106 public final void updateUserSettings(int userId, 107 Function<LocationUserSettings, LocationUserSettings> updater) { 108 getUserSettingsStore(userId).update(updater); 109 } 110 111 /** Dumps info for debugging. */ dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args)112 public final void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { 113 int[] userIds; 114 try { 115 userIds = ActivityManager.getService().getRunningUserIds(); 116 } catch (RemoteException e) { 117 throw e.rethrowFromSystemServer(); 118 } 119 120 if (mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) { 121 ipw.print("ADAS Location Setting: "); 122 ipw.increaseIndent(); 123 if (userIds.length > 1) { 124 ipw.println(); 125 for (int userId : userIds) { 126 ipw.print("[u"); 127 ipw.print(userId); 128 ipw.print("] "); 129 ipw.println(getUserSettings(userId).isAdasGnssLocationEnabled()); 130 } 131 } else { 132 ipw.println(getUserSettings(userIds[0]).isAdasGnssLocationEnabled()); 133 } 134 ipw.decreaseIndent(); 135 } 136 } 137 138 @VisibleForTesting flushFiles()139 final void flushFiles() throws InterruptedException { 140 synchronized (mUserSettings) { 141 int size = mUserSettings.size(); 142 for (int i = 0; i < size; i++) { 143 mUserSettings.valueAt(i).flushFile(); 144 } 145 } 146 } 147 148 @VisibleForTesting deleteFiles()149 final void deleteFiles() throws InterruptedException { 150 synchronized (mUserSettings) { 151 int size = mUserSettings.size(); 152 for (int i = 0; i < size; i++) { 153 mUserSettings.valueAt(i).deleteFile(); 154 } 155 } 156 } 157 fireListeners(int userId, LocationUserSettings oldSettings, LocationUserSettings newSettings)158 protected final void fireListeners(int userId, LocationUserSettings oldSettings, 159 LocationUserSettings newSettings) { 160 for (LocationUserSettingsListener listener : mUserSettingsListeners) { 161 listener.onLocationUserSettingsChanged(userId, oldSettings, newSettings); 162 } 163 } 164 165 class LocationUserSettingsStore extends SettingsStore<LocationUserSettings> { 166 167 protected final int mUserId; 168 LocationUserSettingsStore(int userId, File file)169 LocationUserSettingsStore(int userId, File file) { 170 super(file); 171 mUserId = userId; 172 } 173 174 @Override read(int version, DataInput in)175 protected LocationUserSettings read(int version, DataInput in) throws IOException { 176 return filterSettings(LocationUserSettings.read(mContext.getResources(), version, in)); 177 } 178 179 @Override write(DataOutput out, LocationUserSettings settings)180 protected void write(DataOutput out, LocationUserSettings settings) throws IOException { 181 settings.write(out); 182 } 183 184 @Override update(Function<LocationUserSettings, LocationUserSettings> updater)185 public void update(Function<LocationUserSettings, LocationUserSettings> updater) { 186 super.update(settings -> filterSettings(updater.apply(settings))); 187 } 188 189 @Override onChange(LocationUserSettings oldSettings, LocationUserSettings newSettings)190 protected void onChange(LocationUserSettings oldSettings, 191 LocationUserSettings newSettings) { 192 FgThread.getExecutor().execute(() -> fireListeners(mUserId, oldSettings, newSettings)); 193 } 194 filterSettings(LocationUserSettings settings)195 private LocationUserSettings filterSettings(LocationUserSettings settings) { 196 if (settings.isAdasGnssLocationEnabled() 197 && !mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) { 198 // prevent non-automotive devices from ever enabling this 199 settings = settings.withAdasGnssLocationEnabled(false); 200 } 201 return settings; 202 } 203 } 204 } 205