• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 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 package com.android.angle.common;
17 
18 import android.content.BroadcastReceiver;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.SharedPreferences;
22 import android.database.ContentObserver;
23 import android.net.Uri;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.provider.Settings;
27 import android.text.TextUtils;
28 import android.util.Log;
29 
30 import androidx.preference.PreferenceManager;
31 
32 import org.json.JSONArray;
33 import org.json.JSONException;
34 import org.json.JSONObject;
35 
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.util.ArrayList;
39 import java.util.List;
40 
41 /**
42  * Helper that parses ANGLE rule JSON file and bookkeep the information.
43  *
44  * The format of the ANGLE rule JSON file as shown below.
45  *
46  * {
47  *     "rules":[
48  *         {
49  *             "description": "Applications in this list will use ANGLE",
50  *             "choice": "angle",
51  *             "apps": [
52  *                 {
53  *                     "packageName": "com.android.example.a"
54  *                 },
55  *                 {
56  *                     "packageName": "com.android.example.b"
57  *                 }
58  *             ]
59  *         },
60  *         {
61  *             "description": "Applications in this list will not use ANGLE",
62  *             "choice": "native",
63  *             "apps":[
64  *                 {
65  *                     "packageName": "com.android.example.c"
66  *                 }
67  *             ]
68  *         }
69  *     ]
70  * }
71  */
72 public class AngleRuleHelper
73 {
74 
75     private static final String TAG = "AngleRuleHelper";
76     private static final boolean DEBUG = false;
77     private static final String ANGLE_RULES_FILE = "a4a_rules.json";
78 
79     private static final String ANGLE_JSON_RULES_KEY = "rules";
80     private static final String ANGLE_JSON_DESCRIPTION_KEY = "description";
81     private static final String ANGLE_JSON_CHOICE_KEY = "choice";
82     private static final String ANGLE_JSON_APPS_KEY = "apps";
83     private static final String ANGLE_JSON_PACKAGE_NAME_KEY = "packageName";
84 
85     private List<String> mPackageNamesForNative = new ArrayList<String>();
86     private List<String> mPackageNamesForAngle = new ArrayList<String>();
87 
AngleRuleHelper(Context context)88     public AngleRuleHelper(Context context)
89     {
90         loadRules(context);
91         storeRules(context);
92     }
93 
getPackageNamesForNative()94     List<String> getPackageNamesForNative()
95     {
96         return mPackageNamesForNative;
97     }
98 
getPackageNamesForAngle()99     List<String> getPackageNamesForAngle()
100     {
101         return mPackageNamesForAngle;
102     }
103 
storeRules(Context context)104     private void storeRules(Context context)
105     {
106         final SharedPreferences sharedPreferences =
107             PreferenceManager.getDefaultSharedPreferences(context);
108         final SharedPreferences.Editor editor = sharedPreferences.edit();
109         editor.clear();
110         for (String packageName : mPackageNamesForAngle) {
111             editor.putString(packageName, GlobalSettings.DRIVER_SELECTION_ANGLE);
112         }
113         for (String packageName: mPackageNamesForNative) {
114             editor.putString(packageName, GlobalSettings.DRIVER_SELECTION_NATIVE);
115         }
116         editor.apply();
117     }
118 
loadRules(Context context)119     private void loadRules(Context context)
120     {
121         String jsonStr = null;
122 
123         try
124         {
125             InputStream rulesStream = context.getAssets().open(ANGLE_RULES_FILE);
126             int size = rulesStream.available();
127             byte[] buffer = new byte[size];
128             rulesStream.read(buffer);
129             rulesStream.close();
130             jsonStr = new String(buffer, "UTF-8");
131         }
132         catch (IOException ioe)
133         {
134             Log.e(TAG, "Failed to open " + ANGLE_RULES_FILE + ": ", ioe);
135             return;
136         }
137 
138         if (TextUtils.isEmpty(jsonStr))
139         {
140             Log.v(TAG, "Empty ANGLE rule in: " + ANGLE_RULES_FILE);
141             return;
142         }
143 
144         parseRules(jsonStr);
145     }
146 
parseRules(String rulesJSON)147     private void parseRules(String rulesJSON)
148     {
149         try
150         {
151             final JSONObject jsonObj = new JSONObject(rulesJSON);
152             final JSONArray rules    = jsonObj.getJSONArray(ANGLE_JSON_RULES_KEY);
153             if (rules == null)
154             {
155                 Log.e(TAG, "No ANGLE Rules in " + ANGLE_RULES_FILE);
156                 return;
157             }
158 
159             for (int i = 0; i < rules.length(); i++)
160             {
161                 final JSONObject rule = rules.getJSONObject(i);
162                 Log.v (TAG, "Rule description: " + rule.optString(ANGLE_JSON_DESCRIPTION_KEY));
163 
164                 final String choice = rule.optString(ANGLE_JSON_CHOICE_KEY);
165                 if (TextUtils.isEmpty(choice)
166                         || (!choice.equals("native") && !choice.equals("angle")))
167                 {
168                     Log.v(TAG, "Skipping rule entry due to unknown driver choice.");
169                     continue;
170                 }
171 
172                 final JSONArray apps  = rule.optJSONArray(ANGLE_JSON_APPS_KEY);
173                 if (apps == null || apps.length() == 0)
174                 {
175                     Log.v(TAG, "Skipping rule entry with no apps");
176                     continue;
177                 }
178 
179                 if (choice.equals("native"))
180                 {
181                     mPackageNamesForNative = parsePackageNames(apps);
182                 }
183                 else if (choice.equals("angle"))
184                 {
185                     mPackageNamesForAngle = parsePackageNames(apps);
186                 }
187             }
188         }
189         catch (JSONException je)
190         {
191             Log.e(TAG, "Error when parsing angle JSON: ", je);
192         }
193     }
194 
parsePackageNames(JSONArray apps)195     private List<String> parsePackageNames(JSONArray apps)
196     {
197         List<String> packageNames = new ArrayList<>();
198         for (int j = 0; j < apps.length(); j++)
199         {
200             final JSONObject app = apps.optJSONObject(j);
201             final String packageName = app.optString(ANGLE_JSON_PACKAGE_NAME_KEY);
202             if (DEBUG)
203             {
204                 Log.d(TAG, "parsed package name: " + packageNames);
205             }
206             if (TextUtils.isEmpty(packageName))
207             {
208                 Log.v(TAG, "Skipping empty package name.");
209                 continue;
210             }
211             packageNames.add(packageName);
212         }
213         return packageNames;
214     }
215 }
216