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}