1 /*
2  * Copyright (C) 2008 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.settings;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.Build;
23 import android.os.Bundle;
24 import android.os.PersistableBundle;
25 import android.os.SELinux;
26 import android.os.SystemClock;
27 import android.os.SystemProperties;
28 import android.os.UserHandle;
29 import android.os.UserManager;
30 import android.provider.SearchIndexableResource;
31 import android.provider.Settings;
32 import android.support.v7.preference.Preference;
33 import android.support.v7.preference.PreferenceGroup;
34 import android.telephony.CarrierConfigManager;
35 import android.text.TextUtils;
36 import android.util.Log;
37 import android.widget.Toast;
38 
39 import com.android.internal.logging.MetricsProto.MetricsEvent;
40 import com.android.settings.dashboard.SummaryLoader;
41 import com.android.settings.search.BaseSearchIndexProvider;
42 import com.android.settings.search.Index;
43 import com.android.settings.search.Indexable;
44 import com.android.settingslib.DeviceInfoUtils;
45 import com.android.settingslib.RestrictedLockUtils;
46 
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.List;
50 
51 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
52 
53 public class DeviceInfoSettings extends SettingsPreferenceFragment implements Indexable {
54 
55     private static final String LOG_TAG = "DeviceInfoSettings";
56 
57     private static final String KEY_MANUAL = "manual";
58     private static final String KEY_REGULATORY_INFO = "regulatory_info";
59     private static final String KEY_SYSTEM_UPDATE_SETTINGS = "system_update_settings";
60     private static final String PROPERTY_URL_SAFETYLEGAL = "ro.url.safetylegal";
61     private static final String PROPERTY_SELINUX_STATUS = "ro.build.selinux";
62     private static final String KEY_KERNEL_VERSION = "kernel_version";
63     private static final String KEY_BUILD_NUMBER = "build_number";
64     private static final String KEY_DEVICE_MODEL = "device_model";
65     private static final String KEY_SELINUX_STATUS = "selinux_status";
66     private static final String KEY_BASEBAND_VERSION = "baseband_version";
67     private static final String KEY_FIRMWARE_VERSION = "firmware_version";
68     private static final String KEY_SECURITY_PATCH = "security_patch";
69     private static final String KEY_UPDATE_SETTING = "additional_system_update_settings";
70     private static final String KEY_EQUIPMENT_ID = "fcc_equipment_id";
71     private static final String PROPERTY_EQUIPMENT_ID = "ro.ril.fccid";
72     private static final String KEY_DEVICE_FEEDBACK = "device_feedback";
73     private static final String KEY_SAFETY_LEGAL = "safetylegal";
74 
75     static final int TAPS_TO_BE_A_DEVELOPER = 7;
76 
77     long[] mHits = new long[3];
78     int mDevHitCountdown;
79     Toast mDevHitToast;
80 
81     private UserManager mUm;
82 
83     private EnforcedAdmin mFunDisallowedAdmin;
84     private boolean mFunDisallowedBySystem;
85     private EnforcedAdmin mDebuggingFeaturesDisallowedAdmin;
86     private boolean mDebuggingFeaturesDisallowedBySystem;
87 
88     @Override
getMetricsCategory()89     protected int getMetricsCategory() {
90         return MetricsEvent.DEVICEINFO;
91     }
92 
93     @Override
getHelpResource()94     protected int getHelpResource() {
95         return R.string.help_uri_about;
96     }
97 
98     @Override
onCreate(Bundle icicle)99     public void onCreate(Bundle icicle) {
100         super.onCreate(icicle);
101         mUm = UserManager.get(getActivity());
102 
103         addPreferencesFromResource(R.xml.device_info_settings);
104 
105         setStringSummary(KEY_FIRMWARE_VERSION, Build.VERSION.RELEASE);
106         findPreference(KEY_FIRMWARE_VERSION).setEnabled(true);
107 
108         final String patch = DeviceInfoUtils.getSecurityPatch();
109         if (!TextUtils.isEmpty(patch)) {
110             setStringSummary(KEY_SECURITY_PATCH, patch);
111         } else {
112             getPreferenceScreen().removePreference(findPreference(KEY_SECURITY_PATCH));
113         }
114 
115         setValueSummary(KEY_BASEBAND_VERSION, "gsm.version.baseband");
116         setStringSummary(KEY_DEVICE_MODEL, Build.MODEL + DeviceInfoUtils.getMsvSuffix());
117         setValueSummary(KEY_EQUIPMENT_ID, PROPERTY_EQUIPMENT_ID);
118         setStringSummary(KEY_DEVICE_MODEL, Build.MODEL);
119         setStringSummary(KEY_BUILD_NUMBER, Build.DISPLAY);
120         findPreference(KEY_BUILD_NUMBER).setEnabled(true);
121         findPreference(KEY_KERNEL_VERSION).setSummary(DeviceInfoUtils.getFormattedKernelVersion());
122 
123         if (!SELinux.isSELinuxEnabled()) {
124             String status = getResources().getString(R.string.selinux_status_disabled);
125             setStringSummary(KEY_SELINUX_STATUS, status);
126         } else if (!SELinux.isSELinuxEnforced()) {
127             String status = getResources().getString(R.string.selinux_status_permissive);
128             setStringSummary(KEY_SELINUX_STATUS, status);
129         }
130 
131         // Remove selinux information if property is not present
132         removePreferenceIfPropertyMissing(getPreferenceScreen(), KEY_SELINUX_STATUS,
133                 PROPERTY_SELINUX_STATUS);
134 
135         // Remove Safety information preference if PROPERTY_URL_SAFETYLEGAL is not set
136         removePreferenceIfPropertyMissing(getPreferenceScreen(), KEY_SAFETY_LEGAL,
137                 PROPERTY_URL_SAFETYLEGAL);
138 
139         // Remove Equipment id preference if FCC ID is not set by RIL
140         removePreferenceIfPropertyMissing(getPreferenceScreen(), KEY_EQUIPMENT_ID,
141                 PROPERTY_EQUIPMENT_ID);
142 
143         // Remove Baseband version if wifi-only device
144         if (Utils.isWifiOnly(getActivity())) {
145             getPreferenceScreen().removePreference(findPreference(KEY_BASEBAND_VERSION));
146         }
147 
148         // Dont show feedback option if there is no reporter.
149         if (TextUtils.isEmpty(DeviceInfoUtils.getFeedbackReporterPackage(getActivity()))) {
150             getPreferenceScreen().removePreference(findPreference(KEY_DEVICE_FEEDBACK));
151         }
152 
153         /*
154          * Settings is a generic app and should not contain any device-specific
155          * info.
156          */
157         final Activity act = getActivity();
158 
159         // These are contained by the root preference screen
160         PreferenceGroup parentPreference = getPreferenceScreen();
161 
162         if (mUm.isAdminUser()) {
163             Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference,
164                     KEY_SYSTEM_UPDATE_SETTINGS,
165                     Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
166         } else {
167             // Remove for secondary users
168             removePreference(KEY_SYSTEM_UPDATE_SETTINGS);
169         }
170 
171         // Read platform settings for additional system update setting
172         removePreferenceIfBoolFalse(KEY_UPDATE_SETTING,
173                 R.bool.config_additional_system_update_setting_enable);
174 
175         // Remove manual entry if none present.
176         removePreferenceIfBoolFalse(KEY_MANUAL, R.bool.config_show_manual);
177 
178         // Remove regulatory information if none present.
179         final Intent intent = new Intent(Settings.ACTION_SHOW_REGULATORY_INFO);
180         if (getPackageManager().queryIntentActivities(intent, 0).isEmpty()) {
181             Preference pref = findPreference(KEY_REGULATORY_INFO);
182             if (pref != null) {
183                 getPreferenceScreen().removePreference(pref);
184             }
185         }
186     }
187 
188     @Override
onResume()189     public void onResume() {
190         super.onResume();
191         mDevHitCountdown = getActivity().getSharedPreferences(DevelopmentSettings.PREF_FILE,
192                 Context.MODE_PRIVATE).getBoolean(DevelopmentSettings.PREF_SHOW,
193                         android.os.Build.TYPE.equals("eng")) ? -1 : TAPS_TO_BE_A_DEVELOPER;
194         mDevHitToast = null;
195         mFunDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(
196                 getActivity(), UserManager.DISALLOW_FUN, UserHandle.myUserId());
197         mFunDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(
198                 getActivity(), UserManager.DISALLOW_FUN, UserHandle.myUserId());
199         mDebuggingFeaturesDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(
200                 getActivity(), UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.myUserId());
201         mDebuggingFeaturesDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(
202                 getActivity(), UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.myUserId());
203     }
204 
205     @Override
onPreferenceTreeClick(Preference preference)206     public boolean onPreferenceTreeClick(Preference preference) {
207         if (preference.getKey().equals(KEY_FIRMWARE_VERSION)) {
208             System.arraycopy(mHits, 1, mHits, 0, mHits.length-1);
209             mHits[mHits.length-1] = SystemClock.uptimeMillis();
210             if (mHits[0] >= (SystemClock.uptimeMillis()-500)) {
211                 if (mUm.hasUserRestriction(UserManager.DISALLOW_FUN)) {
212                     if (mFunDisallowedAdmin != null && !mFunDisallowedBySystem) {
213                         RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(),
214                                 mFunDisallowedAdmin);
215                     }
216                     Log.d(LOG_TAG, "Sorry, no fun for you!");
217                     return false;
218                 }
219 
220                 Intent intent = new Intent(Intent.ACTION_MAIN);
221                 intent.setClassName("android",
222                         com.android.internal.app.PlatLogoActivity.class.getName());
223                 try {
224                     startActivity(intent);
225                 } catch (Exception e) {
226                     Log.e(LOG_TAG, "Unable to start activity " + intent.toString());
227                 }
228             }
229         } else if (preference.getKey().equals(KEY_BUILD_NUMBER)) {
230             // Don't enable developer options for secondary users.
231             if (!mUm.isAdminUser()) return true;
232 
233             // Don't enable developer options until device has been provisioned
234             if (!Utils.isDeviceProvisioned(getActivity())) {
235                 return true;
236             }
237 
238             if (mUm.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES)) {
239                 if (mDebuggingFeaturesDisallowedAdmin != null &&
240                         !mDebuggingFeaturesDisallowedBySystem) {
241                     RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(),
242                             mDebuggingFeaturesDisallowedAdmin);
243                 }
244                 return true;
245             }
246 
247             if (mDevHitCountdown > 0) {
248                 mDevHitCountdown--;
249                 if (mDevHitCountdown == 0) {
250                     getActivity().getSharedPreferences(DevelopmentSettings.PREF_FILE,
251                             Context.MODE_PRIVATE).edit().putBoolean(
252                                     DevelopmentSettings.PREF_SHOW, true).apply();
253                     if (mDevHitToast != null) {
254                         mDevHitToast.cancel();
255                     }
256                     mDevHitToast = Toast.makeText(getActivity(), R.string.show_dev_on,
257                             Toast.LENGTH_LONG);
258                     mDevHitToast.show();
259                     // This is good time to index the Developer Options
260                     Index.getInstance(
261                             getActivity().getApplicationContext()).updateFromClassNameResource(
262                                     DevelopmentSettings.class.getName(), true, true);
263 
264                 } else if (mDevHitCountdown > 0
265                         && mDevHitCountdown < (TAPS_TO_BE_A_DEVELOPER-2)) {
266                     if (mDevHitToast != null) {
267                         mDevHitToast.cancel();
268                     }
269                     mDevHitToast = Toast.makeText(getActivity(), getResources().getQuantityString(
270                             R.plurals.show_dev_countdown, mDevHitCountdown, mDevHitCountdown),
271                             Toast.LENGTH_SHORT);
272                     mDevHitToast.show();
273                 }
274             } else if (mDevHitCountdown < 0) {
275                 if (mDevHitToast != null) {
276                     mDevHitToast.cancel();
277                 }
278                 mDevHitToast = Toast.makeText(getActivity(), R.string.show_dev_already,
279                         Toast.LENGTH_LONG);
280                 mDevHitToast.show();
281             }
282         } else if (preference.getKey().equals(KEY_DEVICE_FEEDBACK)) {
283             sendFeedback();
284         } else if(preference.getKey().equals(KEY_SYSTEM_UPDATE_SETTINGS)) {
285             CarrierConfigManager configManager =
286                     (CarrierConfigManager) getSystemService(Context.CARRIER_CONFIG_SERVICE);
287             PersistableBundle b = configManager.getConfig();
288             if (b.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) {
289                 ciActionOnSysUpdate(b);
290             }
291         }
292         return super.onPreferenceTreeClick(preference);
293     }
294 
295     /**
296      * Trigger client initiated action (send intent) on system update
297      */
ciActionOnSysUpdate(PersistableBundle b)298     private void ciActionOnSysUpdate(PersistableBundle b) {
299         String intentStr = b.getString(CarrierConfigManager.
300                 KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING);
301         if (!TextUtils.isEmpty(intentStr)) {
302             String extra = b.getString(CarrierConfigManager.
303                     KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING);
304             String extraVal = b.getString(CarrierConfigManager.
305                     KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING);
306 
307             Intent intent = new Intent(intentStr);
308             if (!TextUtils.isEmpty(extra)) {
309                 intent.putExtra(extra, extraVal);
310             }
311             Log.d(LOG_TAG, "ciActionOnSysUpdate: broadcasting intent " + intentStr +
312                     " with extra " + extra + ", " + extraVal);
313             getActivity().getApplicationContext().sendBroadcast(intent);
314         }
315     }
316 
removePreferenceIfPropertyMissing(PreferenceGroup preferenceGroup, String preference, String property )317     private void removePreferenceIfPropertyMissing(PreferenceGroup preferenceGroup,
318             String preference, String property ) {
319         if (SystemProperties.get(property).equals("")) {
320             // Property is missing so remove preference from group
321             try {
322                 preferenceGroup.removePreference(findPreference(preference));
323             } catch (RuntimeException e) {
324                 Log.d(LOG_TAG, "Property '" + property + "' missing and no '"
325                         + preference + "' preference");
326             }
327         }
328     }
329 
removePreferenceIfBoolFalse(String preference, int resId)330     private void removePreferenceIfBoolFalse(String preference, int resId) {
331         if (!getResources().getBoolean(resId)) {
332             Preference pref = findPreference(preference);
333             if (pref != null) {
334                 getPreferenceScreen().removePreference(pref);
335             }
336         }
337     }
338 
setStringSummary(String preference, String value)339     private void setStringSummary(String preference, String value) {
340         try {
341             findPreference(preference).setSummary(value);
342         } catch (RuntimeException e) {
343             findPreference(preference).setSummary(
344                 getResources().getString(R.string.device_info_default));
345         }
346     }
347 
setValueSummary(String preference, String property)348     private void setValueSummary(String preference, String property) {
349         try {
350             findPreference(preference).setSummary(
351                     SystemProperties.get(property,
352                             getResources().getString(R.string.device_info_default)));
353         } catch (RuntimeException e) {
354             // No recovery
355         }
356     }
357 
sendFeedback()358     private void sendFeedback() {
359         String reporterPackage = DeviceInfoUtils.getFeedbackReporterPackage(getActivity());
360         if (TextUtils.isEmpty(reporterPackage)) {
361             return;
362         }
363         Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
364         intent.setPackage(reporterPackage);
365         startActivityForResult(intent, 0);
366     }
367 
368     private static class SummaryProvider implements SummaryLoader.SummaryProvider {
369 
370         private final Context mContext;
371         private final SummaryLoader mSummaryLoader;
372 
SummaryProvider(Context context, SummaryLoader summaryLoader)373         public SummaryProvider(Context context, SummaryLoader summaryLoader) {
374             mContext = context;
375             mSummaryLoader = summaryLoader;
376         }
377 
378         @Override
setListening(boolean listening)379         public void setListening(boolean listening) {
380             if (listening) {
381                 mSummaryLoader.setSummary(this, mContext.getString(R.string.about_summary,
382                         Build.VERSION.RELEASE));
383             }
384         }
385     }
386 
387     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
388             = new SummaryLoader.SummaryProviderFactory() {
389         @Override
390         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
391                                                                    SummaryLoader summaryLoader) {
392             return new SummaryProvider(activity, summaryLoader);
393         }
394     };
395 
396     /**
397      * For Search.
398      */
399     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
400         new BaseSearchIndexProvider() {
401 
402             @Override
403             public List<SearchIndexableResource> getXmlResourcesToIndex(
404                     Context context, boolean enabled) {
405                 final SearchIndexableResource sir = new SearchIndexableResource(context);
406                 sir.xmlResId = R.xml.device_info_settings;
407                 return Arrays.asList(sir);
408             }
409 
410             @Override
411             public List<String> getNonIndexableKeys(Context context) {
412                 final List<String> keys = new ArrayList<String>();
413                 if (isPropertyMissing(PROPERTY_SELINUX_STATUS)) {
414                     keys.add(KEY_SELINUX_STATUS);
415                 }
416                 if (isPropertyMissing(PROPERTY_URL_SAFETYLEGAL)) {
417                     keys.add(KEY_SAFETY_LEGAL);
418                 }
419                 if (isPropertyMissing(PROPERTY_EQUIPMENT_ID)) {
420                     keys.add(KEY_EQUIPMENT_ID);
421                 }
422                 // Remove Baseband version if wifi-only device
423                 if (Utils.isWifiOnly(context)) {
424                     keys.add((KEY_BASEBAND_VERSION));
425                 }
426                 // Dont show feedback option if there is no reporter.
427                 if (TextUtils.isEmpty(DeviceInfoUtils.getFeedbackReporterPackage(context))) {
428                     keys.add(KEY_DEVICE_FEEDBACK);
429                 }
430                 final UserManager um = UserManager.get(context);
431                 // TODO: system update needs to be fixed for non-owner user b/22760654
432                 if (!um.isAdminUser()) {
433                     keys.add(KEY_SYSTEM_UPDATE_SETTINGS);
434                 }
435                 if (!context.getResources().getBoolean(
436                         R.bool.config_additional_system_update_setting_enable)) {
437                     keys.add(KEY_UPDATE_SETTING);
438                 }
439                 return keys;
440             }
441 
442             private boolean isPropertyMissing(String property) {
443                 return SystemProperties.get(property).equals("");
444             }
445         };
446 
447 }
448