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