153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov/* 253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * Copyright (C) 2016 The Android Open Source Project 353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * 453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * Licensed under the Apache License, Version 2.0 (the "License"); 553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * you may not use this file except in compliance with the License. 653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * You may obtain a copy of the License at 753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * 853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * http://www.apache.org/licenses/LICENSE-2.0 953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * 1053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * Unless required by applicable law or agreed to in writing, software 1153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * distributed under the License is distributed on an "AS IS" BASIS, 1253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * See the License for the specific language governing permissions and 1453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * limitations under the License. 1553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov */ 1653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 1753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovpackage com.android.providers.settings; 1853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 1953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovimport android.os.Bundle; 2053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovimport android.os.UserManager; 2153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovimport android.provider.Settings; 2253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovimport android.util.MemoryIntArray; 2353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovimport android.util.Slog; 2453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovimport android.util.SparseIntArray; 2553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovimport com.android.internal.annotations.GuardedBy; 2653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 2753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovimport java.io.IOException; 2853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 2953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov/** 3053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * This class tracks changes for global/secure/system tables on a 3153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * per user basis and updates a shared memory region which client 3253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov * processes can read to determine if their local caches are stale, 3353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov */ 3453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganovfinal class GenerationRegistry { 359d723d3d573a53173bd8210be20f0ec622eef8fdSvet Ganov private static final String LOG_TAG = "GenerationRegistry"; 3653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 3753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov private static final boolean DEBUG = false; 3853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 3953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov private final Object mLock; 4053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 4153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov @GuardedBy("mLock") 4253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov private final SparseIntArray mKeyToIndexMap = new SparseIntArray(); 4353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 4453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov @GuardedBy("mLock") 4504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov private MemoryIntArray mBackingStore; 4653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 4753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov public GenerationRegistry(Object lock) { 4853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov mLock = lock; 4953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 5053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 5153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov public void incrementGeneration(int key) { 5253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov synchronized (mLock) { 5304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov MemoryIntArray backingStore = getBackingStoreLocked(); 5404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (backingStore != null) { 5553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov try { 5604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore); 5753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (index >= 0) { 5804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov final int generation = backingStore.get(index) + 1; 5904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov backingStore.set(index, generation); 6053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 6153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } catch (IOException e) { 6253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov Slog.e(LOG_TAG, "Error updating generation id", e); 6304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov destroyBackingStore(); 6453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 6553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 6653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 6753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 6853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 6953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov public void addGenerationData(Bundle bundle, int key) { 7053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov synchronized (mLock) { 7104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov MemoryIntArray backingStore = getBackingStoreLocked(); 7204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov try { 7304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (backingStore != null) { 7404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore); 7504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (index >= 0) { 7604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, 7704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov backingStore); 7804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); 798c35dcc7ae956394a6513f4ced8359f56260fbc4Svetoslav Ganov bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, 808c35dcc7ae956394a6513f4ced8359f56260fbc4Svetoslav Ganov backingStore.get(index)); 8104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (DEBUG) { 8204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov Slog.i(LOG_TAG, "Exported index:" + index + " for key:" 8304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov + SettingsProvider.keyToString(key)); 8404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } 8553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 8653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 8704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } catch (IOException e) { 8804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov Slog.e(LOG_TAG, "Error adding generation data", e); 8904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov destroyBackingStore(); 9053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 9153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 9253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 9353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 9404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov public void onUserRemoved(int userId) { 9504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov synchronized (mLock) { 9604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov MemoryIntArray backingStore = getBackingStoreLocked(); 9704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (backingStore != null && mKeyToIndexMap.size() > 0) { 9853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov try { 9904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov final int secureKey = SettingsProvider.makeKey( 10004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov SettingsProvider.SETTINGS_TYPE_SECURE, userId); 10104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov resetSlotForKeyLocked(secureKey, mKeyToIndexMap, backingStore); 10204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov 10304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov final int systemKey = SettingsProvider.makeKey( 10404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov SettingsProvider.SETTINGS_TYPE_SYSTEM, userId); 10504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov resetSlotForKeyLocked(systemKey, mKeyToIndexMap, backingStore); 10653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } catch (IOException e) { 10704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov Slog.e(LOG_TAG, "Error cleaning up for user", e); 10804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov destroyBackingStore(); 10953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 11053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 11153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 11253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 11353a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 11404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov private MemoryIntArray getBackingStoreLocked() { 11504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (mBackingStore == null) { 11604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov // One for the global table, two for system and secure tables for a 11704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov // managed profile (managed profile is not included in the max user 11804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov // count), ten for partially deleted users if users are quickly removed, 11904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov // and twice max user count for system and secure. 12004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov final int size = 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers(); 12104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov try { 12274c9983e80d8224bbe1de7c37e8f6ac4a76df11dSvetoslav Ganov mBackingStore = new MemoryIntArray(size); 1239d723d3d573a53173bd8210be20f0ec622eef8fdSvet Ganov if (DEBUG) { 1249d723d3d573a53173bd8210be20f0ec622eef8fdSvet Ganov Slog.e(LOG_TAG, "Created backing store " + mBackingStore); 1259d723d3d573a53173bd8210be20f0ec622eef8fdSvet Ganov } 12604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } catch (IOException e) { 12704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov Slog.e(LOG_TAG, "Error creating generation tracker", e); 12853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 12953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 13004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov return mBackingStore; 13153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 13253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 13304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov private void destroyBackingStore() { 13404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (mBackingStore != null) { 13553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov try { 13604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov mBackingStore.close(); 1379d723d3d573a53173bd8210be20f0ec622eef8fdSvet Ganov if (DEBUG) { 1389d723d3d573a53173bd8210be20f0ec622eef8fdSvet Ganov Slog.e(LOG_TAG, "Destroyed backing store " + mBackingStore); 1399d723d3d573a53173bd8210be20f0ec622eef8fdSvet Ganov } 14004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } catch (IOException e) { 14104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov Slog.e(LOG_TAG, "Cannot close generation memory array", e); 14204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } 14304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov mBackingStore = null; 14404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } 14504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } 14604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov 14704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov private static void resetSlotForKeyLocked(int key, SparseIntArray keyToIndexMap, 14804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov MemoryIntArray backingStore) throws IOException { 14904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov final int index = keyToIndexMap.get(key, -1); 15004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (index >= 0) { 15104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov keyToIndexMap.delete(key); 15204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov backingStore.set(index, 0); 15304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (DEBUG) { 15404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov Slog.i(LOG_TAG, "Freed index:" + index + " for key:" 15504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov + SettingsProvider.keyToString(key)); 15604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } 15704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } 15804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } 15904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov 16004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov private static int getKeyIndexLocked(int key, SparseIntArray keyToIndexMap, 16104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov MemoryIntArray backingStore) throws IOException { 16204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov int index = keyToIndexMap.get(key, -1); 16304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (index < 0) { 16404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov index = findNextEmptyIndex(backingStore); 16504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (index >= 0) { 16604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov backingStore.set(index, 1); 16704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov keyToIndexMap.append(key, index); 16853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov if (DEBUG) { 16904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov Slog.i(LOG_TAG, "Allocated index:" + index + " for key:" 17053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov + SettingsProvider.keyToString(key)); 17153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 17204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov } else { 17304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov Slog.e(LOG_TAG, "Could not allocate generation index"); 17453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 17553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 17604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov return index; 17753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 17853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov 17904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov private static int findNextEmptyIndex(MemoryIntArray backingStore) throws IOException { 18004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov final int size = backingStore.size(); 18104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov for (int i = 0; i < size; i++) { 18204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov if (backingStore.get(i) == 0) { 18304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov return i; 18453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 18553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 18653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov return -1; 18753a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov } 18853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov}