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