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