1 /* 2 * Copyright (C) 2014 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.cellbroadcastreceiver; 18 19 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE; 20 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_ACTION; 21 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_CLASS; 22 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE; 23 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEY; 24 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_KEYWORDS; 25 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_SCREEN_TITLE; 26 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_RAW_TITLE; 27 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_CLASS_NAME; 28 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_ICON_RESID; 29 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_ACTION; 30 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS; 31 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE; 32 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RANK; 33 import static android.provider.SearchIndexablesContract.COLUMN_INDEX_XML_RES_RESID; 34 import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS; 35 import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS; 36 import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS; 37 38 import android.annotation.Nullable; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.pm.PackageManager; 42 import android.content.res.Resources; 43 import android.database.Cursor; 44 import android.database.MatrixCursor; 45 import android.provider.SearchIndexableResource; 46 import android.provider.SearchIndexablesProvider; 47 import android.telephony.SubscriptionManager; 48 import android.text.TextUtils; 49 50 import com.android.internal.annotations.VisibleForTesting; 51 52 import java.util.ArrayList; 53 import java.util.List; 54 55 public class CellBroadcastSearchIndexableProvider extends SearchIndexablesProvider { 56 57 @VisibleForTesting 58 // Additional keywords for settings search 59 public static final int[] INDEXABLE_KEYWORDS_RESOURCES = { 60 R.string.etws_earthquake_warning, 61 R.string.etws_tsunami_warning, 62 R.string.cmas_presidential_level_alert, 63 R.string.cmas_required_monthly_test, 64 R.string.emergency_alerts_title 65 }; 66 67 @VisibleForTesting 68 public static final SearchIndexableResource[] INDEXABLE_RES = new SearchIndexableResource[] { 69 new SearchIndexableResource(1, R.xml.preferences, 70 CellBroadcastSettings.class.getName(), 71 R.mipmap.ic_launcher_cell_broadcast), 72 }; 73 74 /** 75 * this method is to make this class unit-testable, because super.getContext() is a final 76 * method and therefore not mockable 77 * @return context 78 */ 79 @VisibleForTesting getContextMethod()80 public @Nullable Context getContextMethod() { 81 return super.getContext(); 82 } 83 84 /** 85 * this method is to make this class unit-testable, because 86 * CellBroadcastSettings.getResourcesForDefaultSubId() is a static method and cannot be stubbed. 87 * @return resources 88 */ 89 @VisibleForTesting getResourcesMethod()90 public Resources getResourcesMethod() { 91 return CellBroadcastSettings.getResourcesForDefaultSubId(getContextMethod()); 92 } 93 94 /** 95 * this method is to make this class unit-testable, because 96 * CellBroadcastSettings.isTestAlertsToggleVisible is a static method and therefore not mockable 97 * @return true if test alerts toggle is Visible 98 */ 99 @VisibleForTesting isTestAlertsToggleVisible()100 public boolean isTestAlertsToggleVisible() { 101 return CellBroadcastSettings.isTestAlertsToggleVisible(getContextMethod()); 102 } 103 104 @Override onCreate()105 public boolean onCreate() { 106 return true; 107 } 108 109 @Override queryXmlResources(String[] projection)110 public Cursor queryXmlResources(String[] projection) { 111 if (isAutomotive()) { 112 return null; 113 } 114 115 if (isDisableAllCbMessages()) { 116 return null; 117 } 118 119 MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS); 120 final int count = INDEXABLE_RES.length; 121 for (int n = 0; n < count; n++) { 122 Object[] ref = new Object[7]; 123 ref[COLUMN_INDEX_XML_RES_RANK] = INDEXABLE_RES[n].rank; 124 ref[COLUMN_INDEX_XML_RES_RESID] = INDEXABLE_RES[n].xmlResId; 125 ref[COLUMN_INDEX_XML_RES_CLASS_NAME] = null; 126 ref[COLUMN_INDEX_XML_RES_ICON_RESID] = INDEXABLE_RES[n].iconResId; 127 ref[COLUMN_INDEX_XML_RES_INTENT_ACTION] = Intent.ACTION_MAIN; 128 ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE] = getContextMethod().getPackageName(); 129 ref[COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS] = INDEXABLE_RES[n].className; 130 cursor.addRow(ref); 131 } 132 return cursor; 133 } 134 135 @Override queryRawData(String[] projection)136 public Cursor queryRawData(String[] projection) { 137 if (isAutomotive()) { 138 return null; 139 } 140 141 if (isDisableAllCbMessages()) { 142 return null; 143 } 144 145 MatrixCursor cursor = new MatrixCursor(INDEXABLES_RAW_COLUMNS); 146 final Resources res = getResourcesMethod(); 147 148 Object[] raw = new Object[INDEXABLES_RAW_COLUMNS.length]; 149 raw[COLUMN_INDEX_RAW_TITLE] = res.getString(R.string.sms_cb_settings); 150 List<String> keywordList = new ArrayList<>(); 151 for (int keywordRes : INDEXABLE_KEYWORDS_RESOURCES) { 152 keywordList.add(res.getString(keywordRes)); 153 } 154 155 CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager( 156 getContextMethod(), 157 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); 158 159 if (!channelManager.getCellBroadcastChannelRanges( 160 R.array.public_safety_messages_channels_range_strings).isEmpty()) { 161 keywordList.add(res.getString(R.string.public_safety_message)); 162 } 163 164 if (!channelManager.getCellBroadcastChannelRanges( 165 R.array.state_local_test_alert_range_strings).isEmpty()) { 166 keywordList.add(res.getString(R.string.state_local_test_alert)); 167 } 168 169 raw[COLUMN_INDEX_RAW_KEYWORDS] = TextUtils.join(",", keywordList); 170 171 raw[COLUMN_INDEX_RAW_SCREEN_TITLE] = res.getString(R.string.sms_cb_settings); 172 raw[COLUMN_INDEX_RAW_KEY] = CellBroadcastSettings.class.getSimpleName(); 173 raw[COLUMN_INDEX_RAW_INTENT_ACTION] = Intent.ACTION_MAIN; 174 raw[COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE] = getContextMethod().getPackageName(); 175 raw[COLUMN_INDEX_RAW_INTENT_TARGET_CLASS] = CellBroadcastSettings.class.getName(); 176 177 cursor.addRow(raw); 178 return cursor; 179 } 180 181 @Override queryNonIndexableKeys(String[] projection)182 public Cursor queryNonIndexableKeys(String[] projection) { 183 MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS); 184 185 Resources res = getResourcesMethod(); 186 Object[] ref; 187 188 if (!res.getBoolean(R.bool.show_presidential_alerts_settings)) { 189 ref = new Object[1]; 190 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 191 CellBroadcastSettings.KEY_ENABLE_CMAS_PRESIDENTIAL_ALERTS; 192 cursor.addRow(ref); 193 } 194 195 if (!CellBroadcastSettings.getResources(getContextMethod(), 196 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) 197 .getBoolean(R.bool.show_alert_speech_setting)) { 198 ref = new Object[1]; 199 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 200 CellBroadcastSettings.KEY_ENABLE_ALERT_SPEECH; 201 cursor.addRow(ref); 202 } 203 204 if (!res.getBoolean(R.bool.show_extreme_alert_settings)) { 205 // Remove CMAS preference items in emergency alert category. 206 ref = new Object[1]; 207 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 208 CellBroadcastSettings.KEY_ENABLE_CMAS_EXTREME_THREAT_ALERTS; 209 cursor.addRow(ref); 210 } 211 212 if (!res.getBoolean(R.bool.show_severe_alert_settings)) { 213 214 ref = new Object[1]; 215 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 216 CellBroadcastSettings.KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS; 217 cursor.addRow(ref); 218 } 219 220 if (!res.getBoolean(R.bool.show_amber_alert_settings)) { 221 ref = new Object[1]; 222 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 223 CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS; 224 cursor.addRow(ref); 225 } 226 227 if (!res.getBoolean(R.bool.config_showAreaUpdateInfoSettings)) { 228 ref = new Object[1]; 229 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 230 CellBroadcastSettings.KEY_ENABLE_AREA_UPDATE_INFO_ALERTS; 231 cursor.addRow(ref); 232 } 233 234 CellBroadcastChannelManager channelManager = new CellBroadcastChannelManager( 235 getContextMethod(), SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); 236 if (channelManager.getCellBroadcastChannelRanges( 237 R.array.cmas_amber_alerts_channels_range_strings).isEmpty()) { 238 ref = new Object[1]; 239 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 240 CellBroadcastSettings.KEY_ENABLE_CMAS_AMBER_ALERTS; 241 cursor.addRow(ref); 242 } 243 244 if (channelManager.getCellBroadcastChannelRanges( 245 R.array.emergency_alerts_channels_range_strings).isEmpty()) { 246 ref = new Object[1]; 247 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 248 CellBroadcastSettings.KEY_ENABLE_EMERGENCY_ALERTS; 249 cursor.addRow(ref); 250 } 251 252 if (channelManager.getCellBroadcastChannelRanges( 253 R.array.public_safety_messages_channels_range_strings).isEmpty()) { 254 ref = new Object[1]; 255 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 256 CellBroadcastSettings.KEY_ENABLE_PUBLIC_SAFETY_MESSAGES; 257 cursor.addRow(ref); 258 } 259 260 if (channelManager.getCellBroadcastChannelRanges( 261 R.array.state_local_test_alert_range_strings).isEmpty()) { 262 ref = new Object[1]; 263 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 264 CellBroadcastSettings.KEY_ENABLE_STATE_LOCAL_TEST_ALERTS; 265 cursor.addRow(ref); 266 } 267 268 if (!isTestAlertsToggleVisible()) { 269 ref = new Object[1]; 270 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 271 CellBroadcastSettings.KEY_ENABLE_TEST_ALERTS; 272 } 273 274 if (res.getString(R.string.emergency_alert_second_language_code).isEmpty()) { 275 ref = new Object[1]; 276 ref[COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE] = 277 CellBroadcastSettings.KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE; 278 cursor.addRow(ref); 279 } 280 281 return cursor; 282 } 283 284 /** 285 * Whether or not this is an Android Automotive platform. 286 * @return true if the current platform is automotive 287 */ 288 @VisibleForTesting isAutomotive()289 public boolean isAutomotive() { 290 return getContextMethod().getPackageManager().hasSystemFeature( 291 PackageManager.FEATURE_AUTOMOTIVE); 292 } 293 294 /** 295 * Check disable Cell Broadcast resource. 296 * @return true if Cell Broadcast disable configured by OEM. 297 */ 298 @VisibleForTesting isDisableAllCbMessages()299 public boolean isDisableAllCbMessages() { 300 boolean disable = false; 301 try { 302 Resources res = Resources.getSystem(); 303 int id = res.getIdentifier("config_disable_all_cb_messages", "bool", "android"); 304 disable = res.getBoolean(id); 305 } catch (Exception e) { 306 disable = false; 307 } 308 return disable; 309 } 310 } 311