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 {
3553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov    private static final String LOG_TAG = "GenerationTracker";
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 {
12204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                mBackingStore = new MemoryIntArray(size, false);
12304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            } catch (IOException e) {
12404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                Slog.e(LOG_TAG, "Error creating generation tracker", e);
12553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov            }
12653a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov        }
12704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        return mBackingStore;
12853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov    }
12953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov
13004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov    private void destroyBackingStore() {
13104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        if (mBackingStore != null) {
13253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov            try {
13304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                mBackingStore.close();
13404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            } catch (IOException e) {
13504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                Slog.e(LOG_TAG, "Cannot close generation memory array", e);
13604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            }
13704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            mBackingStore = null;
13804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        }
13904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov    }
14004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov
14104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov    private static void resetSlotForKeyLocked(int key, SparseIntArray keyToIndexMap,
14204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            MemoryIntArray backingStore) throws IOException {
14304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        final int index = keyToIndexMap.get(key, -1);
14404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        if (index >= 0) {
14504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            keyToIndexMap.delete(key);
14604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            backingStore.set(index, 0);
14704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            if (DEBUG) {
14804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                Slog.i(LOG_TAG, "Freed index:" + index + " for key:"
14904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                        + SettingsProvider.keyToString(key));
15004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            }
15104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        }
15204df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov    }
15304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov
15404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov    private static int getKeyIndexLocked(int key, SparseIntArray keyToIndexMap,
15504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            MemoryIntArray backingStore) throws IOException {
15604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        int index = keyToIndexMap.get(key, -1);
15704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        if (index < 0) {
15804df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            index = findNextEmptyIndex(backingStore);
15904df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            if (index >= 0) {
16004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                backingStore.set(index, 1);
16104df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                keyToIndexMap.append(key, index);
16253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov                if (DEBUG) {
16304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                    Slog.i(LOG_TAG, "Allocated index:" + index + " for key:"
16453a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov                            + SettingsProvider.keyToString(key));
16553a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov                }
16604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            } else {
16704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                Slog.e(LOG_TAG, "Could not allocate generation index");
16853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov            }
16953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov        }
17004df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        return index;
17153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov    }
17253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov
17304df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov    private static int findNextEmptyIndex(MemoryIntArray backingStore) throws IOException {
17404df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        final int size = backingStore.size();
17504df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov        for (int i = 0; i < size; i++) {
17604df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov            if (backingStore.get(i) == 0) {
17704df738bcb6584dd82b731a67f4cf8d6925b061eSvetoslav Ganov                return i;
17853a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov            }
17953a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov        }
18053a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov        return -1;
18153a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov    }
18253a441ca8eda5a3e6209a952b1bbd32a39e19a1cSvet Ganov}