1 /* 2 * Copyright (C) 2021 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.tv.settings.library.about; 18 19 import static com.android.tv.settings.library.ManagerUtil.STATE_SYSTEM_ABOUT; 20 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.ResolveInfo; 26 import android.os.Build; 27 import android.os.Bundle; 28 import android.os.PersistableBundle; 29 import android.os.SELinux; 30 import android.os.SystemClock; 31 import android.os.SystemProperties; 32 import android.os.UserHandle; 33 import android.os.UserManager; 34 import android.provider.Settings; 35 import android.telephony.CarrierConfigManager; 36 import android.text.TextUtils; 37 import android.util.Log; 38 import android.widget.Toast; 39 40 import androidx.annotation.Keep; 41 import androidx.annotation.Nullable; 42 43 import com.android.tv.settings.library.PreferenceCompat; 44 import com.android.tv.settings.library.State; 45 import com.android.tv.settings.library.UIUpdateCallback; 46 import com.android.tv.settings.library.data.PreferenceCompatManager; 47 import com.android.tv.settings.library.settingslib.RestrictedLockUtils; 48 import com.android.tv.settings.library.settingslib.RestrictedLockUtilsInternal; 49 import com.android.tv.settings.library.util.LibUtils; 50 import com.android.tv.settings.library.util.PreferenceCompatUtils; 51 import com.android.tv.settings.library.util.ResourcesUtil; 52 53 import java.util.ArrayList; 54 import java.util.List; 55 56 /** 57 * The "About" screen in TV settings. 58 */ 59 @Keep 60 public class AboutState implements State { 61 private static final String TAG = "AboutFragment"; 62 63 private static final String KEY_MANUAL = "manual"; 64 private static final String KEY_REGULATORY_INFO = "regulatory_info"; 65 private static final String KEY_SYSTEM_UPDATE_SETTINGS = "system_update_settings"; 66 private static final String PROPERTY_URL_SAFETYLEGAL = "ro.url.safetylegal"; 67 private static final String PROPERTY_SELINUX_STATUS = "ro.build.selinux"; 68 private static final String KEY_KERNEL_VERSION = "kernel_version"; 69 private static final String KEY_BUILD_NUMBER = "build_number"; 70 private static final String KEY_DEVICE_MODEL = "device_model"; 71 private static final String KEY_SELINUX_STATUS = "selinux_status"; 72 private static final String KEY_BASEBAND_VERSION = "baseband_version"; 73 private static final String KEY_FIRMWARE_VERSION = "firmware_version"; 74 private static final String KEY_SECURITY_PATCH = "security_patch"; 75 private static final String KEY_UPDATE_SETTING = "additional_system_update_settings"; 76 private static final String KEY_EQUIPMENT_ID = "fcc_equipment_id"; 77 private static final String PROPERTY_EQUIPMENT_ID = "ro.ril.fccid"; 78 private static final String KEY_DEVICE_FEEDBACK = "device_feedback"; 79 private static final String KEY_SAFETY_LEGAL = "safetylegal"; 80 private static final String KEY_DEVICE_NAME = "device_name"; 81 private static final String KEY_TUTORIALS = "tutorials"; 82 private static final String KEY_RESET = "reset"; 83 private static final String KEY_RESET_OPTIONS = "reset_options"; 84 85 static final int TAPS_TO_BE_A_DEVELOPER = 7; 86 87 long[] mHits = new long[3]; 88 int mDevHitCountdown; 89 Toast mDevHitToast; 90 91 private UserManager mUm; 92 93 private final BroadcastReceiver mDeviceNameReceiver = new BroadcastReceiver() { 94 @Override 95 public void onReceive(Context context, Intent intent) { 96 refreshDeviceName(); 97 } 98 }; 99 100 private final Context context; 101 private final UIUpdateCallback uiUpdateCallback; 102 private PreferenceCompat mFirmwareVersionPref; 103 private PreferenceCompat mDeviceTutorialsPref; 104 private PreferenceCompat mMDeviceTutorialsPref; 105 AboutState(Context context, UIUpdateCallback uiUpdateCallback)106 public AboutState(Context context, UIUpdateCallback uiUpdateCallback) { 107 this.context = context; 108 this.uiUpdateCallback = uiUpdateCallback; 109 } 110 111 private PreferenceCompatManager mPreferenceCompatManager; 112 113 @Override onAttach()114 public void onAttach() { 115 // no-op 116 } 117 118 @Override onCreate(Bundle extra)119 public void onCreate(Bundle extra) { 120 mUm = UserManager.get(context); 121 122 mPreferenceCompatManager = new PreferenceCompatManager(); 123 refreshDeviceName(); 124 final PreferenceCompat deviceNamePref = mPreferenceCompatManager.getOrCreatePrefCompat( 125 KEY_DEVICE_NAME); 126 PreferenceCompatUtils.resolveSystemActivityOrRemove(context, preferenceCompats, 127 deviceNamePref, 0); 128 129 mFirmwareVersionPref = mPreferenceCompatManager.getOrCreatePrefCompat(KEY_FIRMWARE_VERSION); 130 mFirmwareVersionPref.setSummary(Build.VERSION.RELEASE_OR_CODENAME); 131 mFirmwareVersionPref.setEnabled(true); 132 133 final PreferenceCompat securityPatchPref = mPreferenceCompatManager.getOrCreatePrefCompat( 134 KEY_SECURITY_PATCH); 135 final String patch = DeviceInfoUtils.getSecurityPatch(); 136 if (!TextUtils.isEmpty(patch)) { 137 securityPatchPref.setSummary(patch); 138 } else { 139 removePreference(securityPatchPref); 140 } 141 142 mPreferenceCompatManager.getOrCreatePrefCompat(KEY_DEVICE_MODEL).setSummary( 143 Build.MODEL + DeviceInfoUtils.getMsvSuffix()); 144 mPreferenceCompatManager.getOrCreatePrefCompat(KEY_EQUIPMENT_ID) 145 .setSummary(getSystemPropertySummary(PROPERTY_EQUIPMENT_ID)); 146 147 final PreferenceCompat buildNumberPref = mPreferenceCompatManager.getOrCreatePrefCompat( 148 KEY_BUILD_NUMBER); 149 buildNumberPref.setSummary(Build.DISPLAY); 150 buildNumberPref.setEnabled(true); 151 mPreferenceCompatManager.getOrCreatePrefCompat(KEY_KERNEL_VERSION) 152 .setSummary(DeviceInfoUtils.getFormattedKernelVersion(context)); 153 154 final PreferenceCompat selinuxPref = mPreferenceCompatManager.getOrCreatePrefCompat( 155 KEY_SELINUX_STATUS); 156 if (!SELinux.isSELinuxEnabled()) { 157 selinuxPref.setSummary(ResourcesUtil.getString(context, "selinux_status_disabled")); 158 } else if (!SELinux.isSELinuxEnforced()) { 159 selinuxPref.setSummary(ResourcesUtil.getString(context, "selinux_status_permissive")); 160 } 161 162 // Remove selinux information if property is not present 163 if (TextUtils.isEmpty(SystemProperties.get(PROPERTY_SELINUX_STATUS))) { 164 removePreference(selinuxPref); 165 } 166 167 // Remove Safety information preference if PROPERTY_URL_SAFETYLEGAL is not set 168 if (TextUtils.isEmpty(SystemProperties.get(PROPERTY_URL_SAFETYLEGAL))) { 169 removePreference(mPreferenceCompatManager.getOrCreatePrefCompat(KEY_SAFETY_LEGAL)); 170 } 171 172 // Remove Equipment id preference if FCC ID is not set by RIL 173 if (TextUtils.isEmpty(SystemProperties.get(PROPERTY_EQUIPMENT_ID))) { 174 removePreference(mPreferenceCompatManager.getOrCreatePrefCompat(KEY_EQUIPMENT_ID)); 175 } 176 177 // Remove Baseband version if wifi-only device 178 if (Utils.isWifiOnly(context)) { 179 removePreference(mPreferenceCompatManager.getOrCreatePrefCompat(KEY_BASEBAND_VERSION)); 180 } 181 182 // Don't show feedback option if there is no reporter. 183 if (TextUtils.isEmpty(DeviceInfoUtils.getFeedbackReporterPackage(context))) { 184 removePreference(mPreferenceCompatManager.getOrCreatePrefCompat(KEY_DEVICE_FEEDBACK)); 185 } 186 187 final PreferenceCompat resetPreference = mPreferenceCompatManager.getOrCreatePrefCompat( 188 KEY_RESET); 189 resetPreference.setContentDescription( 190 ResourcesUtil.getString(context, "factory_reset_content_description")); 191 192 // Don't show the reset options if factory reset is restricted 193 final PreferenceCompat resetOptionsPreference = 194 mPreferenceCompatManager.getOrCreatePrefCompat(KEY_RESET_OPTIONS); 195 if (resetOptionsPreference != null 196 && RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context, 197 UserManager.DISALLOW_FACTORY_RESET, UserHandle.myUserId()) != null) { 198 // TODO (b/194102677): Handle setFragment(null); 199 // resetOptionsPreference.setFragment(null); 200 } 201 202 final PreferenceCompat updateSettingsPref = mPreferenceCompatManager.getOrCreatePrefCompat( 203 KEY_SYSTEM_UPDATE_SETTINGS); 204 updateSettingsPref.setContentDescription( 205 ResourcesUtil.getString(context, "system_update_content_description")); 206 207 if (mUm.isAdminUser()) { 208 final Intent systemUpdateIntent = new Intent(Settings.ACTION_SYSTEM_UPDATE_SETTINGS); 209 final ResolveInfo info = 210 LibUtils.systemIntentIsHandled(context, systemUpdateIntent); 211 if (info == null) { 212 removePreference(updateSettingsPref); 213 } else { 214 updateSettingsPref.setTitle(info.loadLabel(context.getPackageManager()).toString()); 215 } 216 } else if (updateSettingsPref != null) { 217 // Remove for secondary users 218 removePreference(updateSettingsPref); 219 } 220 221 // Read platform settings for additional system update setting 222 if (!ResourcesUtil.getBoolean(context, "config_additional_system_update_setting_enable")) { 223 removePreference(mPreferenceCompatManager.getOrCreatePrefCompat(KEY_UPDATE_SETTING)); 224 } 225 226 // Remove manual entry if none present. 227 if (!ResourcesUtil.getBoolean(context, "config_show_manual")) { 228 removePreference(mPreferenceCompatManager.getOrCreatePrefCompat(KEY_MANUAL)); 229 } 230 231 // Remove regulatory information if none present. 232 final PreferenceCompat regulatoryPref = mPreferenceCompatManager.getOrCreatePrefCompat( 233 KEY_REGULATORY_INFO); 234 PreferenceCompatUtils.resolveSystemActivityOrRemove(context, preferenceCompats, 235 regulatoryPref, 0); 236 237 if (uiUpdateCallback != null) { 238 uiUpdateCallback.notifyUpdateAll(getStateIdentifier(), preferenceCompats); 239 } 240 } 241 242 List<PreferenceCompat> preferenceCompats = new ArrayList<>(); 243 removePreference(@ullable PreferenceCompat preference)244 private void removePreference(@Nullable PreferenceCompat preference) { 245 if (preference != null) { 246 preferenceCompats.remove(preference); 247 } 248 } 249 250 @Override onStart()251 public void onStart() { 252 refreshDeviceName(); 253 254 context.registerReceiver(mDeviceNameReceiver, 255 new IntentFilter(DeviceManager.ACTION_DEVICE_NAME_UPDATE), 256 Context.RECEIVER_EXPORTED_UNAUDITED); 257 } 258 259 @Override onResume()260 public void onResume() { 261 mDevHitCountdown = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context) 262 ? -1 : TAPS_TO_BE_A_DEVELOPER; 263 mDevHitToast = null; 264 updateTutorials(); 265 } 266 267 @Override onPause()268 public void onPause() { 269 270 } 271 272 @Override onStop()273 public void onStop() { 274 context.unregisterReceiver(mDeviceNameReceiver); 275 } 276 277 @Override onDestroy()278 public void onDestroy() { 279 280 } 281 282 @Override onDetach()283 public void onDetach() { 284 285 } 286 refreshDeviceName()287 private void refreshDeviceName() { 288 final PreferenceCompat deviceNamePref = mPreferenceCompatManager.getOrCreatePrefCompat( 289 KEY_DEVICE_NAME); 290 if (deviceNamePref != null) { 291 deviceNamePref.setSummary(DeviceManager.getDeviceName(context)); 292 } 293 } 294 295 @Override onPreferenceTreeClick(String[] key, boolean status)296 public boolean onPreferenceTreeClick(String[] key, boolean status) { 297 boolean handled = true; 298 switch (key[0]) { 299 case KEY_FIRMWARE_VERSION: 300 System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1); 301 mHits[mHits.length - 1] = SystemClock.uptimeMillis(); 302 if (mHits[0] >= (SystemClock.uptimeMillis() - 500)) { 303 if (mUm.hasUserRestriction(UserManager.DISALLOW_FUN)) { 304 final RestrictedLockUtils.EnforcedAdmin admin = RestrictedLockUtilsInternal 305 .checkIfRestrictionEnforced(context, UserManager.DISALLOW_FUN, 306 UserHandle.myUserId()); 307 if (admin != null) { 308 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, 309 admin); 310 } 311 312 Log.d(TAG, "Sorry, no fun for you!"); 313 return false; 314 } 315 316 Intent intent = new Intent(Intent.ACTION_MAIN); 317 intent.setClassName("android", 318 "PlatLogoActivity"); 319 try { 320 context.startActivity(intent); 321 } catch (Exception e) { 322 Log.e(TAG, "Unable to start activity " + intent.toString()); 323 } 324 } 325 break; 326 case KEY_BUILD_NUMBER: 327 // logEntrySelected(TvSettingsEnums.SYSTEM_ABOUT_BUILD); 328 // Don't enable developer options for secondary users. 329 if (!mUm.isAdminUser()) { 330 return true; 331 } 332 333 if (mUm.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES)) { 334 final RestrictedLockUtils.EnforcedAdmin admin = RestrictedLockUtilsInternal 335 .checkIfRestrictionEnforced(context, 336 UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.myUserId()); 337 if (admin != null) { 338 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, admin); 339 } 340 return true; 341 } 342 343 if (mDevHitCountdown > 0) { 344 mDevHitCountdown--; 345 if (mDevHitCountdown == 0) { 346 DevelopmentSettingsEnabler 347 .setDevelopmentSettingsEnabled(context, true); 348 if (mDevHitToast != null) { 349 mDevHitToast.cancel(); 350 } 351 mDevHitToast = Toast.makeText(context, 352 ResourcesUtil.getString(context, "show_dev_on"), 353 Toast.LENGTH_LONG); 354 mDevHitToast.show(); 355 // This is good time to index the Developer Options 356 // Index.getInstance( 357 // getActivity().getApplicationContext()).updateFromClassNameResource( 358 // DevelopmentSettings.class.getName(), true, true); 359 } else if (mDevHitCountdown > 0 360 && mDevHitCountdown < (TAPS_TO_BE_A_DEVELOPER - 2)) { 361 if (mDevHitToast != null) { 362 mDevHitToast.cancel(); 363 } 364 mDevHitToast = Toast 365 .makeText(context, ResourcesUtil.getQuantityString( 366 context, "show_dev_countdown", mDevHitCountdown, 367 mDevHitCountdown), 368 Toast.LENGTH_SHORT); 369 mDevHitToast.show(); 370 } 371 } else if (mDevHitCountdown < 0) { 372 if (mDevHitToast != null) { 373 mDevHitToast.cancel(); 374 } 375 mDevHitToast = Toast.makeText(context, 376 ResourcesUtil.getString(context, "show_dev_already"), 377 Toast.LENGTH_LONG); 378 mDevHitToast.show(); 379 } 380 break; 381 case KEY_DEVICE_FEEDBACK: 382 sendFeedback(); 383 break; 384 case KEY_SYSTEM_UPDATE_SETTINGS: 385 // logEntrySelected(TvSettingsEnums.SYSTEM_ABOUT_SYSTEM_UPDATE); 386 CarrierConfigManager configManager = (CarrierConfigManager) 387 context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 388 PersistableBundle b = configManager.getConfig(); 389 if (b != null && 390 b.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) { 391 ciActionOnSysUpdate(b); 392 } 393 context.startActivity(new Intent(Settings.ACTION_SYSTEM_UPDATE_SETTINGS)); 394 break; 395 case KEY_DEVICE_NAME: 396 // logEntrySelected(TvSettingsEnums.SYSTEM_ABOUT_DEVICE_NAME); 397 break; 398 case KEY_RESET: 399 // logEntrySelected(TvSettingsEnums.SYSTEM_ABOUT_FACTORY_RESET); 400 Intent factoryResetIntent = new Intent(); 401 factoryResetIntent.setClassName( 402 "com.android.tv.settings", 403 "com.android.tv.settings.device.storage.ResetActivity"); 404 context.startActivity(factoryResetIntent); 405 break; 406 default: 407 handled = false; 408 } 409 return handled; 410 } 411 412 @Override onActivityResult(int requestCode, int resultCode, Intent data)413 public void onActivityResult(int requestCode, int resultCode, Intent data) { 414 415 } 416 417 @Override onPreferenceChange(String[] key, Object newValue)418 public boolean onPreferenceChange(String[] key, Object newValue) { 419 return false; 420 } 421 422 @Override getStateIdentifier()423 public int getStateIdentifier() { 424 return STATE_SYSTEM_ABOUT; 425 } 426 427 /** 428 * Trigger client initiated action (send intent) on system update 429 */ ciActionOnSysUpdate(PersistableBundle b)430 private void ciActionOnSysUpdate(PersistableBundle b) { 431 String intentStr = b.getString(CarrierConfigManager. 432 KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING); 433 if (!TextUtils.isEmpty(intentStr)) { 434 String extra = b.getString(CarrierConfigManager. 435 KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING); 436 String extraVal = b.getString(CarrierConfigManager. 437 KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING); 438 439 Intent intent = new Intent(intentStr); 440 if (!TextUtils.isEmpty(extra)) { 441 intent.putExtra(extra, extraVal); 442 } 443 Log.d(TAG, "ciActionOnSysUpdate: broadcasting intent " + intentStr + 444 " with extra " + extra + ", " + extraVal); 445 context.getApplicationContext().sendBroadcast(intent); 446 } 447 } 448 getSystemPropertySummary(String property)449 private String getSystemPropertySummary(String property) { 450 return SystemProperties.get(property, 451 ResourcesUtil.getString(context, "device_info_default")); 452 } 453 sendFeedback()454 private void sendFeedback() { 455 String reporterPackage = DeviceInfoUtils.getFeedbackReporterPackage(context); 456 if (TextUtils.isEmpty(reporterPackage)) { 457 return; 458 } 459 Intent intent = new Intent(Intent.ACTION_BUG_REPORT); 460 intent.setPackage(reporterPackage); 461 context.startActivity(intent); 462 } 463 updateTutorials()464 private void updateTutorials() { 465 mMDeviceTutorialsPref = mPreferenceCompatManager.getOrCreatePrefCompat(KEY_TUTORIALS); 466 if (mMDeviceTutorialsPref != null) { 467 final ResolveInfo info = LibUtils.systemIntentIsHandled(context, 468 mMDeviceTutorialsPref.getIntent()); 469 mMDeviceTutorialsPref.setVisible(info != null); 470 if (info != null) { 471 mMDeviceTutorialsPref.setTitle( 472 info.loadLabel(context.getPackageManager()).toString()); 473 } 474 } 475 } 476 477 @Override onDisplayDialogPreference(String[] key)478 public void onDisplayDialogPreference(String[] key) { 479 480 } 481 } 482