blob: 20c67f87d3b9be140d6c0e12f166c6f4814c297f [file] [log] [blame]
/*
* Copyright (C) 2022 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.server.pm;
import android.os.Process;
import android.util.Log;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.WatchedArrayList;
import com.android.server.utils.WatchedSparseArray;
import com.android.server.utils.Watcher;
/**
* A wrapper over {@link WatchedArrayList} that tracks the current (app ID -> SettingBase) mapping
* for non-system apps. Also tracks system app settings in an {@link WatchedSparseArray}.
*/
final class AppIdSettingMap {
/**
* We use an ArrayList instead of an SparseArray for non system apps because the number of apps
* might be big, and only ArrayList gives us a constant lookup time. For a given app ID, the
* index to the corresponding SettingBase object is (appId - FIRST_APPLICATION_ID). If an app ID
* doesn't exist (i.e., app is not installed), we fill the corresponding entry with null.
*/
private final WatchedArrayList<SettingBase> mNonSystemSettings;
private final SnapshotCache<WatchedArrayList<SettingBase>> mNonSystemSettingsSnapshot;
private final WatchedSparseArray<SettingBase> mSystemSettings;
private final SnapshotCache<WatchedSparseArray<SettingBase>> mSystemSettingsSnapshot;
private int mFirstAvailableAppId = Process.FIRST_APPLICATION_UID;
AppIdSettingMap() {
mNonSystemSettings = new WatchedArrayList<>();
mNonSystemSettingsSnapshot = new SnapshotCache.Auto<>(
mNonSystemSettings, mNonSystemSettings, "AppIdSettingMap.mNonSystemSettings");
mSystemSettings = new WatchedSparseArray<>();
mSystemSettingsSnapshot = new SnapshotCache.Auto<>(
mSystemSettings, mSystemSettings, "AppIdSettingMap.mSystemSettings");
}
AppIdSettingMap(AppIdSettingMap orig) {
mNonSystemSettings = orig.mNonSystemSettingsSnapshot.snapshot();
mNonSystemSettingsSnapshot = new SnapshotCache.Sealed<>();
mSystemSettings = orig.mSystemSettingsSnapshot.snapshot();
mSystemSettingsSnapshot = new SnapshotCache.Sealed<>();
}
/** Returns true if the requested AppID was valid and not already registered. */
public boolean registerExistingAppId(int appId, SettingBase setting, Object name) {
if (appId >= Process.FIRST_APPLICATION_UID) {
int size = mNonSystemSettings.size();
final int index = appId - Process.FIRST_APPLICATION_UID;
// fill the array until our index becomes valid
while (index >= size) {
mNonSystemSettings.add(null);
size++;
}
if (mNonSystemSettings.get(index) != null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Adding duplicate app id: " + appId
+ " name=" + name);
return false;
}
mNonSystemSettings.set(index, setting);
} else {
if (mSystemSettings.get(appId) != null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Adding duplicate shared id: " + appId
+ " name=" + name);
return false;
}
mSystemSettings.put(appId, setting);
}
return true;
}
public SettingBase getSetting(int appId) {
if (appId >= Process.FIRST_APPLICATION_UID) {
final int size = mNonSystemSettings.size();
final int index = appId - Process.FIRST_APPLICATION_UID;
return index < size ? mNonSystemSettings.get(index) : null;
} else {
return mSystemSettings.get(appId);
}
}
public void removeSetting(int appId) {
if (appId >= Process.FIRST_APPLICATION_UID) {
final int size = mNonSystemSettings.size();
final int index = appId - Process.FIRST_APPLICATION_UID;
if (index < size) {
mNonSystemSettings.set(index, null);
}
} else {
mSystemSettings.remove(appId);
}
setFirstAvailableAppId(appId + 1);
}
// This should be called (at least) whenever an application is removed
private void setFirstAvailableAppId(int uid) {
if (uid > mFirstAvailableAppId) {
mFirstAvailableAppId = uid;
}
}
public void replaceSetting(int appId, SettingBase setting) {
if (appId >= Process.FIRST_APPLICATION_UID) {
final int size = mNonSystemSettings.size();
final int index = appId - Process.FIRST_APPLICATION_UID;
if (index < size) {
mNonSystemSettings.set(index, setting);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: calling replaceAppIdLpw to"
+ " replace SettingBase at appId=" + appId
+ " but nothing is replaced.");
}
} else {
mSystemSettings.put(appId, setting);
}
}
/** Returns a new AppID or -1 if we could not find an available AppID to assign */
public int acquireAndRegisterNewAppId(SettingBase obj) {
final int size = mNonSystemSettings.size();
for (int i = mFirstAvailableAppId - Process.FIRST_APPLICATION_UID; i < size; i++) {
if (mNonSystemSettings.get(i) == null) {
mNonSystemSettings.set(i, obj);
return Process.FIRST_APPLICATION_UID + i;
}
}
// None left?
if (size > (Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID)) {
return -1;
}
mNonSystemSettings.add(obj);
return Process.FIRST_APPLICATION_UID + size;
}
public AppIdSettingMap snapshot() {
return new AppIdSettingMap(this);
}
public void registerObserver(Watcher observer) {
mNonSystemSettings.registerObserver(observer);
mSystemSettings.registerObserver(observer);
}
}