1 /* 2 * Copyright 2019 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.util.Log; 28 29 import androidx.preference.PreferenceManager; 30 31 import org.json.JSONArray; 32 import org.json.JSONException; 33 import org.json.JSONObject; 34 35 import java.io.IOException; 36 import java.io.InputStream; 37 38 public class Receiver extends BroadcastReceiver 39 { 40 41 private static final String TAG = "AngleReceiver"; 42 private static final String ANGLE_RULES_FILE = "a4a_rules.json"; 43 44 @Override onReceive(Context context, Intent intent)45 public void onReceive(Context context, Intent intent) 46 { 47 String action = intent.getAction(); 48 Log.v(TAG, "Received intent: '" + action + "'"); 49 50 if (action.equals(context.getString(R.string.intent_angle_for_android_toast_message))) 51 { 52 Bundle results = getResultExtras(true); 53 results.putString(context.getString(R.string.intent_key_a4a_toast_message), 54 context.getString(R.string.angle_in_use_toast_message)); 55 } 56 else 57 { 58 String jsonStr = loadRules(context); 59 String packageNames = parsePackageNames(jsonStr); 60 61 // Update the ANGLE allowlist 62 if (packageNames != null) 63 { 64 GlobalSettings.updateAngleAllowlist(context, packageNames); 65 } 66 67 updateDeveloperOptionsWatcher(context); 68 } 69 } 70 71 /* 72 * Open the rules file and pull all the JSON into a string 73 */ loadRules(Context context)74 private String loadRules(Context context) 75 { 76 String jsonStr = null; 77 78 try 79 { 80 InputStream rulesStream = context.getAssets().open(ANGLE_RULES_FILE); 81 int size = rulesStream.available(); 82 byte[] buffer = new byte[size]; 83 rulesStream.read(buffer); 84 rulesStream.close(); 85 jsonStr = new String(buffer, "UTF-8"); 86 } 87 catch (IOException ioe) 88 { 89 Log.e(TAG, "Failed to open " + ANGLE_RULES_FILE + ": ", ioe); 90 } 91 92 return jsonStr; 93 } 94 95 /* 96 * Extract all app package names from the json file and return them comma separated 97 */ parsePackageNames(String rulesJSON)98 private String parsePackageNames(String rulesJSON) 99 { 100 StringBuilder packageNames = new StringBuilder(); 101 102 try 103 { 104 JSONObject jsonObj = new JSONObject(rulesJSON); 105 JSONArray rules = jsonObj.getJSONArray("Rules"); 106 if (rules == null) 107 { 108 Log.e(TAG, "No Rules in " + ANGLE_RULES_FILE); 109 return null; 110 } 111 for (int i = 0; i < rules.length(); i++) 112 { 113 JSONObject rule = rules.getJSONObject(i); 114 JSONArray apps = rule.optJSONArray("Applications"); 115 if (apps == null) 116 { 117 Log.v(TAG, "Skipping Rules entry with no Applications"); 118 continue; 119 } 120 for (int j = 0; j < apps.length(); j++) 121 { 122 JSONObject app = apps.optJSONObject(j); 123 String appName = app.optString("AppName"); 124 if ((appName == null) || appName.isEmpty()) 125 { 126 Log.e(TAG, "Invalid AppName: '" + appName + "'"); 127 } 128 if (!packageNames.toString().isEmpty()) 129 { 130 packageNames.append(","); 131 } 132 packageNames.append(appName); 133 } 134 } 135 Log.v(TAG, "Parsed the following package names from " + ANGLE_RULES_FILE + ": " 136 + packageNames); 137 } 138 catch (JSONException je) 139 { 140 Log.e(TAG, "Error when parsing angle JSON: ", je); 141 return null; 142 } 143 144 return packageNames.toString(); 145 } 146 updateAllUseAngle(Context context)147 static void updateAllUseAngle(Context context) 148 { 149 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 150 String allUseAngleKey = context.getString(R.string.pref_key_all_angle); 151 boolean allUseAngle = prefs.getBoolean(allUseAngleKey, false); 152 153 GlobalSettings.updateAllUseAngle(context, allUseAngle); 154 155 Log.v(TAG, "All PKGs use ANGLE set to: " + allUseAngle); 156 } 157 updateShowAngleInUseDialogBox(Context context)158 static void updateShowAngleInUseDialogBox(Context context) 159 { 160 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 161 String showAngleInUseDialogBoxKey = 162 context.getString(R.string.pref_key_angle_in_use_dialog); 163 boolean showAngleInUseDialogBox = prefs.getBoolean(showAngleInUseDialogBoxKey, false); 164 165 GlobalSettings.updateShowAngleInUseDialog(context, showAngleInUseDialogBox); 166 167 Log.v(TAG, "Show 'ANGLE In Use' dialog box set to: " + showAngleInUseDialogBox); 168 } 169 170 /** 171 * When Developer Options are disabled, reset all of the global settings back to their defaults. 172 */ updateDeveloperOptionsWatcher(Context context)173 private static void updateDeveloperOptionsWatcher(Context context) 174 { 175 Uri settingUri = Settings.Global.getUriFor(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED); 176 177 ContentObserver developerOptionsObserver = new ContentObserver(new Handler()) { 178 @Override 179 public void onChange(boolean selfChange) 180 { 181 super.onChange(selfChange); 182 183 boolean developerOptionsEnabled = 184 (1 185 == Settings.Global.getInt(context.getContentResolver(), 186 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0)); 187 188 Log.v(TAG, "Developer Options enabled value changed: " 189 + "developerOptionsEnabled = " + developerOptionsEnabled); 190 191 if (!developerOptionsEnabled) 192 { 193 // Reset the necessary settings to their defaults. 194 SharedPreferences.Editor editor = 195 PreferenceManager.getDefaultSharedPreferences(context).edit(); 196 editor.clear(); 197 editor.apply(); 198 GlobalSettings.clearAllGlobalSettings(context); 199 } 200 } 201 }; 202 203 context.getContentResolver().registerContentObserver( 204 settingUri, false, developerOptionsObserver); 205 developerOptionsObserver.onChange(true); 206 } 207 } 208