blob: 044ec867aef430a8193f5eaec9c8fe76dec29bcb [file] [log] [blame]
Svet Ganov53a441c2016-04-19 19:38:00 -07001/*
2 * Copyright (C) 2016 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
17package com.android.providers.settings;
18
19import android.os.Bundle;
20import android.os.UserManager;
21import android.provider.Settings;
22import android.util.MemoryIntArray;
23import android.util.Slog;
24import android.util.SparseIntArray;
25import com.android.internal.annotations.GuardedBy;
26
27import java.io.IOException;
28
29/**
30 * This class tracks changes for global/secure/system tables on a
31 * per user basis and updates a shared memory region which client
32 * processes can read to determine if their local caches are stale,
33 */
34final class GenerationRegistry {
Svet Ganov9d723d32016-08-27 11:05:56 -070035 private static final String LOG_TAG = "GenerationRegistry";
Svet Ganov53a441c2016-04-19 19:38:00 -070036
37 private static final boolean DEBUG = false;
38
39 private final Object mLock;
40
41 @GuardedBy("mLock")
42 private final SparseIntArray mKeyToIndexMap = new SparseIntArray();
43
44 @GuardedBy("mLock")
Svetoslav Ganov04df7382016-05-10 18:55:47 -070045 private MemoryIntArray mBackingStore;
Svet Ganov53a441c2016-04-19 19:38:00 -070046
47 public GenerationRegistry(Object lock) {
48 mLock = lock;
Svet Ganov53a441c2016-04-19 19:38:00 -070049 }
50
51 public void incrementGeneration(int key) {
52 synchronized (mLock) {
Svetoslav Ganov04df7382016-05-10 18:55:47 -070053 MemoryIntArray backingStore = getBackingStoreLocked();
54 if (backingStore != null) {
Svet Ganov53a441c2016-04-19 19:38:00 -070055 try {
Svetoslav Ganov04df7382016-05-10 18:55:47 -070056 final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore);
Svet Ganov53a441c2016-04-19 19:38:00 -070057 if (index >= 0) {
Svetoslav Ganov04df7382016-05-10 18:55:47 -070058 final int generation = backingStore.get(index) + 1;
59 backingStore.set(index, generation);
Svet Ganov53a441c2016-04-19 19:38:00 -070060 }
61 } catch (IOException e) {
62 Slog.e(LOG_TAG, "Error updating generation id", e);
Svetoslav Ganov04df7382016-05-10 18:55:47 -070063 destroyBackingStore();
Svet Ganov53a441c2016-04-19 19:38:00 -070064 }
65 }
66 }
67 }
68
69 public void addGenerationData(Bundle bundle, int key) {
70 synchronized (mLock) {
Svetoslav Ganov04df7382016-05-10 18:55:47 -070071 MemoryIntArray backingStore = getBackingStoreLocked();
72 try {
73 if (backingStore != null) {
74 final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore);
75 if (index >= 0) {
76 bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY,
77 backingStore);
78 bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
Svetoslav Ganov8c35dcc2016-06-17 15:27:43 -070079 bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
80 backingStore.get(index));
Svetoslav Ganov04df7382016-05-10 18:55:47 -070081 if (DEBUG) {
82 Slog.i(LOG_TAG, "Exported index:" + index + " for key:"
83 + SettingsProvider.keyToString(key));
84 }
Svet Ganov53a441c2016-04-19 19:38:00 -070085 }
86 }
Svetoslav Ganov04df7382016-05-10 18:55:47 -070087 } catch (IOException e) {
88 Slog.e(LOG_TAG, "Error adding generation data", e);
89 destroyBackingStore();
90 }
91 }
92 }
93
94 public void onUserRemoved(int userId) {
95 synchronized (mLock) {
96 MemoryIntArray backingStore = getBackingStoreLocked();
97 if (backingStore != null && mKeyToIndexMap.size() > 0) {
98 try {
99 final int secureKey = SettingsProvider.makeKey(
100 SettingsProvider.SETTINGS_TYPE_SECURE, userId);
101 resetSlotForKeyLocked(secureKey, mKeyToIndexMap, backingStore);
102
103 final int systemKey = SettingsProvider.makeKey(
104 SettingsProvider.SETTINGS_TYPE_SYSTEM, userId);
105 resetSlotForKeyLocked(systemKey, mKeyToIndexMap, backingStore);
106 } catch (IOException e) {
107 Slog.e(LOG_TAG, "Error cleaning up for user", e);
108 destroyBackingStore();
109 }
Svet Ganov53a441c2016-04-19 19:38:00 -0700110 }
111 }
112 }
113
Andreas Gampeb58893072018-09-05 16:52:31 -0700114 @GuardedBy("mLock")
Svetoslav Ganov04df7382016-05-10 18:55:47 -0700115 private MemoryIntArray getBackingStoreLocked() {
116 if (mBackingStore == null) {
117 // One for the global table, two for system and secure tables for a
118 // managed profile (managed profile is not included in the max user
119 // count), ten for partially deleted users if users are quickly removed,
120 // and twice max user count for system and secure.
121 final int size = 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers();
122 try {
Svetoslav Ganov74c99832016-12-05 20:07:20 -0800123 mBackingStore = new MemoryIntArray(size);
Svet Ganov9d723d32016-08-27 11:05:56 -0700124 if (DEBUG) {
125 Slog.e(LOG_TAG, "Created backing store " + mBackingStore);
126 }
Svetoslav Ganov04df7382016-05-10 18:55:47 -0700127 } catch (IOException e) {
128 Slog.e(LOG_TAG, "Error creating generation tracker", e);
129 }
130 }
131 return mBackingStore;
132 }
133
134 private void destroyBackingStore() {
135 if (mBackingStore != null) {
136 try {
137 mBackingStore.close();
Svet Ganov9d723d32016-08-27 11:05:56 -0700138 if (DEBUG) {
139 Slog.e(LOG_TAG, "Destroyed backing store " + mBackingStore);
140 }
Svetoslav Ganov04df7382016-05-10 18:55:47 -0700141 } catch (IOException e) {
142 Slog.e(LOG_TAG, "Cannot close generation memory array", e);
143 }
144 mBackingStore = null;
145 }
146 }
147
148 private static void resetSlotForKeyLocked(int key, SparseIntArray keyToIndexMap,
149 MemoryIntArray backingStore) throws IOException {
150 final int index = keyToIndexMap.get(key, -1);
151 if (index >= 0) {
152 keyToIndexMap.delete(key);
153 backingStore.set(index, 0);
154 if (DEBUG) {
155 Slog.i(LOG_TAG, "Freed index:" + index + " for key:"
156 + SettingsProvider.keyToString(key));
157 }
158 }
159 }
160
161 private static int getKeyIndexLocked(int key, SparseIntArray keyToIndexMap,
162 MemoryIntArray backingStore) throws IOException {
163 int index = keyToIndexMap.get(key, -1);
Svet Ganov53a441c2016-04-19 19:38:00 -0700164 if (index < 0) {
Svetoslav Ganov04df7382016-05-10 18:55:47 -0700165 index = findNextEmptyIndex(backingStore);
Svet Ganov53a441c2016-04-19 19:38:00 -0700166 if (index >= 0) {
Svetoslav Ganov04df7382016-05-10 18:55:47 -0700167 backingStore.set(index, 1);
168 keyToIndexMap.append(key, index);
169 if (DEBUG) {
170 Slog.i(LOG_TAG, "Allocated index:" + index + " for key:"
171 + SettingsProvider.keyToString(key));
Svet Ganov53a441c2016-04-19 19:38:00 -0700172 }
173 } else {
174 Slog.e(LOG_TAG, "Could not allocate generation index");
175 }
176 }
177 return index;
178 }
179
Svetoslav Ganov04df7382016-05-10 18:55:47 -0700180 private static int findNextEmptyIndex(MemoryIntArray backingStore) throws IOException {
181 final int size = backingStore.size();
182 for (int i = 0; i < size; i++) {
183 if (backingStore.get(i) == 0) {
184 return i;
Svet Ganov53a441c2016-04-19 19:38:00 -0700185 }
186 }
Svet Ganov53a441c2016-04-19 19:38:00 -0700187 return -1;
188 }
189}