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