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