1 /*
2  * Copyright (C) 2022 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.pm;
18 
19 import android.os.Process;
20 import android.util.Log;
21 
22 import com.android.server.utils.SnapshotCache;
23 import com.android.server.utils.WatchedArrayList;
24 import com.android.server.utils.WatchedSparseArray;
25 import com.android.server.utils.Watcher;
26 
27 /**
28  * A wrapper over {@link WatchedArrayList} that tracks the current (app ID -> SettingBase) mapping
29  * for non-system apps. Also tracks system app settings in an {@link WatchedSparseArray}.
30  */
31 final class AppIdSettingMap {
32     /**
33      * We use an ArrayList instead of an SparseArray for non system apps because the number of apps
34      * might be big, and only ArrayList gives us a constant lookup time. For a given app ID, the
35      * index to the corresponding SettingBase object is (appId - FIRST_APPLICATION_ID). If an app ID
36      * doesn't exist (i.e., app is not installed), we fill the corresponding entry with null.
37      */
38     private final WatchedArrayList<SettingBase> mNonSystemSettings;
39     private final SnapshotCache<WatchedArrayList<SettingBase>> mNonSystemSettingsSnapshot;
40     private final WatchedSparseArray<SettingBase> mSystemSettings;
41     private final SnapshotCache<WatchedSparseArray<SettingBase>> mSystemSettingsSnapshot;
42     private int mFirstAvailableAppId = Process.FIRST_APPLICATION_UID;
43 
AppIdSettingMap()44     AppIdSettingMap() {
45         mNonSystemSettings = new WatchedArrayList<>();
46         mNonSystemSettingsSnapshot = new SnapshotCache.Auto<>(
47                 mNonSystemSettings, mNonSystemSettings, "AppIdSettingMap.mNonSystemSettings");
48         mSystemSettings = new WatchedSparseArray<>();
49         mSystemSettingsSnapshot = new SnapshotCache.Auto<>(
50                 mSystemSettings, mSystemSettings, "AppIdSettingMap.mSystemSettings");
51     }
52 
AppIdSettingMap(AppIdSettingMap orig)53     AppIdSettingMap(AppIdSettingMap orig) {
54         mNonSystemSettings = orig.mNonSystemSettingsSnapshot.snapshot();
55         mNonSystemSettingsSnapshot = new SnapshotCache.Sealed<>();
56         mSystemSettings = orig.mSystemSettingsSnapshot.snapshot();
57         mSystemSettingsSnapshot = new SnapshotCache.Sealed<>();
58     }
59 
60     /** Returns true if the requested AppID was valid and not already registered. */
registerExistingAppId(int appId, SettingBase setting, Object name)61     public boolean registerExistingAppId(int appId, SettingBase setting, Object name) {
62         if (appId >= Process.FIRST_APPLICATION_UID) {
63             int size = mNonSystemSettings.size();
64             final int index = appId - Process.FIRST_APPLICATION_UID;
65             // fill the array until our index becomes valid
66             while (index >= size) {
67                 mNonSystemSettings.add(null);
68                 size++;
69             }
70             if (mNonSystemSettings.get(index) != null) {
71                 PackageManagerService.reportSettingsProblem(Log.WARN,
72                         "Adding duplicate app id: " + appId
73                                 + " name=" + name);
74                 return false;
75             }
76             mNonSystemSettings.set(index, setting);
77         } else {
78             if (mSystemSettings.get(appId) != null) {
79                 PackageManagerService.reportSettingsProblem(Log.WARN,
80                         "Adding duplicate shared id: " + appId
81                                 + " name=" + name);
82                 return false;
83             }
84             mSystemSettings.put(appId, setting);
85         }
86         return true;
87     }
88 
getSetting(int appId)89     public SettingBase getSetting(int appId) {
90         if (appId >= Process.FIRST_APPLICATION_UID) {
91             final int size = mNonSystemSettings.size();
92             final int index = appId - Process.FIRST_APPLICATION_UID;
93             return index < size ? mNonSystemSettings.get(index) : null;
94         } else {
95             return mSystemSettings.get(appId);
96         }
97     }
98 
removeSetting(int appId)99     public void removeSetting(int appId) {
100         if (appId >= Process.FIRST_APPLICATION_UID) {
101             final int size = mNonSystemSettings.size();
102             final int index = appId - Process.FIRST_APPLICATION_UID;
103             if (index < size) {
104                 mNonSystemSettings.set(index, null);
105             }
106         } else {
107             mSystemSettings.remove(appId);
108         }
109         setFirstAvailableAppId(appId + 1);
110     }
111 
112     // This should be called (at least) whenever an application is removed
setFirstAvailableAppId(int uid)113     private void setFirstAvailableAppId(int uid) {
114         if (uid > mFirstAvailableAppId) {
115             mFirstAvailableAppId = uid;
116         }
117     }
118 
replaceSetting(int appId, SettingBase setting)119     public void replaceSetting(int appId, SettingBase setting) {
120         if (appId >= Process.FIRST_APPLICATION_UID) {
121             final int size = mNonSystemSettings.size();
122             final int index = appId - Process.FIRST_APPLICATION_UID;
123             if (index < size) {
124                 mNonSystemSettings.set(index, setting);
125             } else {
126                 PackageManagerService.reportSettingsProblem(Log.WARN,
127                         "Error in package manager settings: calling replaceAppIdLpw to"
128                                 + " replace SettingBase at appId=" + appId
129                                 + " but nothing is replaced.");
130             }
131         } else {
132             mSystemSettings.put(appId, setting);
133         }
134     }
135 
136     /** Returns a new AppID or -1 if we could not find an available AppID to assign */
acquireAndRegisterNewAppId(SettingBase obj)137     public int acquireAndRegisterNewAppId(SettingBase obj) {
138         final int size = mNonSystemSettings.size();
139         for (int i = mFirstAvailableAppId - Process.FIRST_APPLICATION_UID; i < size; i++) {
140             if (mNonSystemSettings.get(i) == null) {
141                 mNonSystemSettings.set(i, obj);
142                 return Process.FIRST_APPLICATION_UID + i;
143             }
144         }
145 
146         // None left?
147         if (size > (Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID)) {
148             return -1;
149         }
150 
151         mNonSystemSettings.add(obj);
152         return Process.FIRST_APPLICATION_UID + size;
153     }
154 
snapshot()155     public AppIdSettingMap snapshot() {
156         return new AppIdSettingMap(this);
157     }
158 
registerObserver(Watcher observer)159     public void registerObserver(Watcher observer) {
160         mNonSystemSettings.registerObserver(observer);
161         mSystemSettings.registerObserver(observer);
162     }
163 }
164