1/*
2 * Copyright (C) 2018 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.server.am;
18
19import android.content.ContentResolver;
20import android.database.ContentObserver;
21import android.net.Uri;
22import android.os.Build;
23import android.os.SystemProperties;
24import android.provider.Settings;
25import android.text.TextUtils;
26import android.util.Slog;
27import android.view.ThreadedRenderer;
28
29import com.android.internal.annotations.VisibleForTesting;
30import com.android.internal.util.Preconditions;
31
32/**
33 * Maps global system settings to system properties.
34 * <p>The properties are dynamically updated when settings change.
35 */
36class GlobalSettingsToPropertiesMapper {
37
38    private static final String TAG = "GlobalSettingsToPropertiesMapper";
39
40    // List mapping entries in the following format:
41    // {Settings.Global.SETTING_NAME, "system_property_name"}
42    // Important: Property being added should be whitelisted by SELinux policy or have one of the
43    // already whitelisted prefixes in system_server.te, e.g. sys.
44    private static final String[][] sGlobalSettingsMapping = new String[][] {
45        {Settings.Global.SYS_VDSO, "sys.vdso"},
46        {Settings.Global.FPS_DEVISOR, ThreadedRenderer.DEBUG_FPS_DIVISOR},
47        {Settings.Global.DISPLAY_PANEL_LPM, "sys.display_panel_lpm"},
48        {Settings.Global.SYS_UIDCPUPOWER, "sys.uidcpupower"},
49        {Settings.Global.SYS_TRACED, "sys.traced.enable_override"},
50    };
51
52
53    private final ContentResolver mContentResolver;
54    private final String[][] mGlobalSettingsMapping;
55
56    @VisibleForTesting
57    GlobalSettingsToPropertiesMapper(ContentResolver contentResolver,
58            String[][] globalSettingsMapping) {
59        mContentResolver = contentResolver;
60        mGlobalSettingsMapping = globalSettingsMapping;
61    }
62
63    void updatePropertiesFromGlobalSettings() {
64        for (String[] entry : mGlobalSettingsMapping) {
65            final String settingName = entry[0];
66            final String propName = entry[1];
67            Uri settingUri = Settings.Global.getUriFor(settingName);
68            Preconditions.checkNotNull(settingUri, "Setting " + settingName + " not found");
69            ContentObserver co = new ContentObserver(null) {
70                @Override
71                public void onChange(boolean selfChange) {
72                    updatePropertyFromSetting(settingName, propName);
73                }
74            };
75            updatePropertyFromSetting(settingName, propName);
76            mContentResolver.registerContentObserver(settingUri, false, co);
77        }
78    }
79
80    public static void start(ContentResolver contentResolver) {
81        new GlobalSettingsToPropertiesMapper(contentResolver, sGlobalSettingsMapping)
82                .updatePropertiesFromGlobalSettings();
83    }
84
85    private String getGlobalSetting(String name) {
86        return Settings.Global.getString(mContentResolver, name);
87    }
88
89    private void setProperty(String key, String value) {
90        // Check if need to clear the property
91        if (value == null) {
92            // It's impossible to remove system property, therefore we check previous value to
93            // avoid setting an empty string if the property wasn't set.
94            if (TextUtils.isEmpty(systemPropertiesGet(key))) {
95                return;
96            }
97            value = "";
98        }
99        try {
100            systemPropertiesSet(key, value);
101        } catch (Exception e) {
102            // Failure to set a property can be caused by SELinux denial. This usually indicates
103            // that the property wasn't whitelisted in sepolicy.
104            // No need to report it on all user devices, only on debug builds.
105            if (Build.IS_DEBUGGABLE) {
106                Slog.wtf(TAG, "Unable to set property " + key + " value '" + value + "'", e);
107            } else {
108                Slog.e(TAG, "Unable to set property " + key + " value '" + value + "'", e);
109            }
110        }
111    }
112
113    @VisibleForTesting
114    protected String systemPropertiesGet(String key) {
115        return SystemProperties.get(key);
116    }
117
118    @VisibleForTesting
119    protected void systemPropertiesSet(String key, String value) {
120        SystemProperties.set(key, value);
121    }
122
123    @VisibleForTesting
124    void updatePropertyFromSetting(String settingName, String propName) {
125        String settingValue = getGlobalSetting(settingName);
126        setProperty(propName, settingValue);
127    }
128}
129