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 
17 package com.android.car;
18 
19 import android.annotation.Nullable;
20 import android.annotation.RawRes;
21 import android.car.settings.ICarConfigurationManager;
22 import android.car.settings.SpeedBumpConfiguration;
23 import android.content.Context;
24 import android.util.Log;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 
28 import org.json.JSONException;
29 import org.json.JSONObject;
30 
31 import java.io.PrintWriter;
32 
33 /**
34  * A service that will look at a default JSON configuration file on the system and parses out its
35  * results.
36  *
37  * <p>This service will look for the JSON file that is mapped to {@code R.raw.car_config}. If this
38  * value does not exist or is malformed, then this service will not fail; instead, it returns
39  * default values for various configurations.
40  */
41 public class CarConfigurationService extends ICarConfigurationManager.Stub
42         implements CarServiceBase {
43     private static final String TAG = "CarConfigurationService";
44 
45     // Keys for accessing data in the parsed JSON related to SpeedBump.
46     @VisibleForTesting
47     static final String SPEED_BUMP_CONFIG_KEY = "SpeedBump";
48     @VisibleForTesting
49     static final String SPEED_BUMP_ACQUIRED_PERMITS_PER_SECOND_KEY =
50             "acquiredPermitsPerSecond";
51     @VisibleForTesting
52     static final String SPEED_BUMP_MAX_PERMIT_POOL_KEY = "maxPermitPool";
53     @VisibleForTesting
54     static final String SPEED_BUMP_PERMIT_FILL_DELAY_KEY = "permitFillDelay";
55 
56     // Default values for speed bump configuration.
57     @VisibleForTesting
58     static final double DEFAULT_SPEED_BUMP_ACQUIRED_PERMITS_PER_SECOND = 0.5d;
59     @VisibleForTesting
60     static final double DEFAULT_SPEED_BUMP_MAX_PERMIT_POOL = 5d;
61     @VisibleForTesting
62     static final long DEFAULT_SPEED_BUMP_PERMIT_FILL_DELAY = 600L;
63 
64     private final Context mContext;
65     private final JsonReader mJsonReader;
66 
67     @VisibleForTesting
68     @Nullable
69     JSONObject mConfigFile;
70 
71     @Nullable
72     private SpeedBumpConfiguration mSpeedBumpConfiguration;
73 
74     /**
75      * An interface that abstracts away the parsing of a JSON file. This interface allows the
76      * JSON file to be mocked away for testing.
77      */
78     @VisibleForTesting
79     interface JsonReader {
80         /**
81          * Returns the contents of the JSON file that is pointed to by the given {@code resId} as
82          * a string.
83          *
84          * @param context The current Context.
85          * @param resId The resource id of the JSON file.
86          * @return A string representation of the file or {@code null} if an error occurred.
87          */
88         @Nullable
jsonFileToString(Context context, @RawRes int resId)89         String jsonFileToString(Context context, @RawRes int resId);
90     }
91 
CarConfigurationService(Context context, JsonReader reader)92     CarConfigurationService(Context context, JsonReader reader) {
93         mContext = context;
94         mJsonReader = reader;
95     }
96 
97     /**
98      * Returns the configuration values for speed bump that is found in the configuration JSON on
99      * the system. If there was an error reading this JSON or the JSON did not contain
100      * speed bump configuration, then default values will be returned. This method does not return
101      * {@code null}.
102      */
103     @Override
getSpeedBumpConfiguration()104     public SpeedBumpConfiguration getSpeedBumpConfiguration() {
105         if (mSpeedBumpConfiguration == null) {
106             return getDefaultSpeedBumpConfiguration();
107         }
108         return mSpeedBumpConfiguration;
109     }
110 
111     @Override
init()112     public synchronized void init() {
113         String jsonString = mJsonReader.jsonFileToString(mContext, R.raw.car_config);
114         if (jsonString != null) {
115             try {
116                 mConfigFile = new JSONObject(jsonString);
117             } catch (JSONException e) {
118                 Log.e(TAG, "Error reading JSON file", e);
119             }
120         }
121 
122         mSpeedBumpConfiguration = createSpeedBumpConfiguration();
123     }
124 
125     @Override
release()126     public synchronized void release() {
127         mConfigFile =  null;
128         mSpeedBumpConfiguration = null;
129     }
130 
131     @Override
dump(PrintWriter writer)132     public void dump(PrintWriter writer) {
133         writer.println("*CarConfigurationService*");
134         writer.println("Config value initialized: " + (mConfigFile != null));
135         if (mConfigFile != null) {
136             try {
137                 writer.println("Config: " + mConfigFile.toString(/* indentSpaces= */ 2));
138             } catch (JSONException e) {
139                 Log.e(TAG, "Error printing JSON config", e);
140                 writer.println("Config: " + mConfigFile);
141             }
142         }
143 
144         writer.println("SpeedBumpConfig initialized: " + (mSpeedBumpConfiguration != null));
145         if (mSpeedBumpConfiguration != null) {
146             writer.println("SpeedBumpConfig: " + mSpeedBumpConfiguration);
147         }
148     }
149 
150     /**
151      * Reads the configuration for speed bump off of the parsed JSON stored in {@link #mConfigFile}.
152      * If {@code mConfigFile} is {@code null} or a configuration does not exist for speed bump,
153      * then return the default configuration created by {@link #getDefaultSpeedBumpConfiguration()}.
154      */
createSpeedBumpConfiguration()155     private SpeedBumpConfiguration createSpeedBumpConfiguration() {
156         if (mConfigFile == null) {
157             return getDefaultSpeedBumpConfiguration();
158         }
159 
160         try {
161             JSONObject speedBumpJson = mConfigFile.getJSONObject(SPEED_BUMP_CONFIG_KEY);
162 
163             if (speedBumpJson != null) {
164                 return new SpeedBumpConfiguration(
165                         speedBumpJson.getDouble(SPEED_BUMP_ACQUIRED_PERMITS_PER_SECOND_KEY),
166                         speedBumpJson.getDouble(SPEED_BUMP_MAX_PERMIT_POOL_KEY),
167                         speedBumpJson.getLong(SPEED_BUMP_PERMIT_FILL_DELAY_KEY));
168             }
169         } catch (JSONException e) {
170             Log.e(TAG, "Error parsing SpeedBumpConfiguration; returning default values", e);
171         }
172 
173         // If an error is encountered or the JSON does not contain an entry for speed bump
174         // configuration, then return default values.
175         return getDefaultSpeedBumpConfiguration();
176     }
177 
getDefaultSpeedBumpConfiguration()178     private SpeedBumpConfiguration getDefaultSpeedBumpConfiguration() {
179         return new SpeedBumpConfiguration(
180                 DEFAULT_SPEED_BUMP_ACQUIRED_PERMITS_PER_SECOND,
181                 DEFAULT_SPEED_BUMP_MAX_PERMIT_POOL,
182                 DEFAULT_SPEED_BUMP_PERMIT_FILL_DELAY);
183     }
184 }
185