1 /* 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 17 package com.android.providers.settings; 18 19 import android.os.Bundle; 20 import android.os.UserManager; 21 import android.provider.Settings; 22 import android.util.MemoryIntArray; 23 import android.util.Slog; 24 import android.util.SparseIntArray; 25 26 import com.android.internal.annotations.GuardedBy; 27 28 import java.io.IOException; 29 30 /** 31 * This class tracks changes for config/global/secure/system tables 32 * on a per user basis and updates a shared memory region which 33 * client processes can read to determine if their local caches are 34 * stale. 35 */ 36 final class GenerationRegistry { 37 private static final String LOG_TAG = "GenerationRegistry"; 38 39 private static final boolean DEBUG = false; 40 41 private final Object mLock; 42 43 @GuardedBy("mLock") 44 private final SparseIntArray mKeyToIndexMap = new SparseIntArray(); 45 46 @GuardedBy("mLock") 47 private MemoryIntArray mBackingStore; 48 GenerationRegistry(Object lock)49 public GenerationRegistry(Object lock) { 50 mLock = lock; 51 } 52 incrementGeneration(int key)53 public void incrementGeneration(int key) { 54 synchronized (mLock) { 55 MemoryIntArray backingStore = getBackingStoreLocked(); 56 if (backingStore != null) { 57 try { 58 final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore); 59 if (index >= 0) { 60 final int generation = backingStore.get(index) + 1; 61 backingStore.set(index, generation); 62 } 63 } catch (IOException e) { 64 Slog.e(LOG_TAG, "Error updating generation id", e); 65 destroyBackingStore(); 66 } 67 } 68 } 69 } 70 addGenerationData(Bundle bundle, int key)71 public void addGenerationData(Bundle bundle, int key) { 72 synchronized (mLock) { 73 MemoryIntArray backingStore = getBackingStoreLocked(); 74 try { 75 if (backingStore != null) { 76 final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore); 77 if (index >= 0) { 78 bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, 79 backingStore); 80 bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); 81 bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, 82 backingStore.get(index)); 83 if (DEBUG) { 84 Slog.i(LOG_TAG, "Exported index:" + index + " for key:" 85 + SettingsProvider.keyToString(key)); 86 } 87 } 88 } 89 } catch (IOException e) { 90 Slog.e(LOG_TAG, "Error adding generation data", e); 91 destroyBackingStore(); 92 } 93 } 94 } 95 onUserRemoved(int userId)96 public void onUserRemoved(int userId) { 97 synchronized (mLock) { 98 MemoryIntArray backingStore = getBackingStoreLocked(); 99 if (backingStore != null && mKeyToIndexMap.size() > 0) { 100 try { 101 final int secureKey = SettingsProvider.makeKey( 102 SettingsProvider.SETTINGS_TYPE_SECURE, userId); 103 resetSlotForKeyLocked(secureKey, mKeyToIndexMap, backingStore); 104 105 final int systemKey = SettingsProvider.makeKey( 106 SettingsProvider.SETTINGS_TYPE_SYSTEM, userId); 107 resetSlotForKeyLocked(systemKey, mKeyToIndexMap, backingStore); 108 } catch (IOException e) { 109 Slog.e(LOG_TAG, "Error cleaning up for user", e); 110 destroyBackingStore(); 111 } 112 } 113 } 114 } 115 116 @GuardedBy("mLock") getBackingStoreLocked()117 private MemoryIntArray getBackingStoreLocked() { 118 if (mBackingStore == null) { 119 // One for the config table, one for the global table, two for system 120 // and secure tables for a managed profile (managed profile is not 121 // included in the max user count), ten for partially deleted users if 122 // users are quickly removed, and twice max user count for system and 123 // secure. 124 final int size = 1 + 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers(); 125 try { 126 mBackingStore = new MemoryIntArray(size); 127 if (DEBUG) { 128 Slog.e(LOG_TAG, "Created backing store " + mBackingStore); 129 } 130 } catch (IOException e) { 131 Slog.e(LOG_TAG, "Error creating generation tracker", e); 132 } 133 } 134 return mBackingStore; 135 } 136 destroyBackingStore()137 private void destroyBackingStore() { 138 if (mBackingStore != null) { 139 try { 140 mBackingStore.close(); 141 if (DEBUG) { 142 Slog.e(LOG_TAG, "Destroyed backing store " + mBackingStore); 143 } 144 } catch (IOException e) { 145 Slog.e(LOG_TAG, "Cannot close generation memory array", e); 146 } 147 mBackingStore = null; 148 } 149 } 150 resetSlotForKeyLocked(int key, SparseIntArray keyToIndexMap, MemoryIntArray backingStore)151 private static void resetSlotForKeyLocked(int key, SparseIntArray keyToIndexMap, 152 MemoryIntArray backingStore) throws IOException { 153 final int index = keyToIndexMap.get(key, -1); 154 if (index >= 0) { 155 keyToIndexMap.delete(key); 156 backingStore.set(index, 0); 157 if (DEBUG) { 158 Slog.i(LOG_TAG, "Freed index:" + index + " for key:" 159 + SettingsProvider.keyToString(key)); 160 } 161 } 162 } 163 getKeyIndexLocked(int key, SparseIntArray keyToIndexMap, MemoryIntArray backingStore)164 private static int getKeyIndexLocked(int key, SparseIntArray keyToIndexMap, 165 MemoryIntArray backingStore) throws IOException { 166 int index = keyToIndexMap.get(key, -1); 167 if (index < 0) { 168 index = findNextEmptyIndex(backingStore); 169 if (index >= 0) { 170 backingStore.set(index, 1); 171 keyToIndexMap.append(key, index); 172 if (DEBUG) { 173 Slog.i(LOG_TAG, "Allocated index:" + index + " for key:" 174 + SettingsProvider.keyToString(key)); 175 } 176 } else { 177 Slog.e(LOG_TAG, "Could not allocate generation index"); 178 } 179 } 180 return index; 181 } 182 findNextEmptyIndex(MemoryIntArray backingStore)183 private static int findNextEmptyIndex(MemoryIntArray backingStore) throws IOException { 184 final int size = backingStore.size(); 185 for (int i = 0; i < size; i++) { 186 if (backingStore.get(i) == 0) { 187 return i; 188 } 189 } 190 return -1; 191 } 192 }