OSDN Git Service

Block developer settings during SUW
[android-x86/packages-apps-Settings.git] / src / com / android / settings / DeviceInfoSettings.java
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.content.pm.ApplicationInfo;
23 import android.content.pm.PackageManager;
24 import android.content.pm.ResolveInfo;
25 import android.os.Build;
26 import android.os.Bundle;
27 import android.os.PersistableBundle;
28 import android.os.SELinux;
29 import android.os.SystemClock;
30 import android.os.SystemProperties;
31 import android.os.UserHandle;
32 import android.os.UserManager;
33 import android.preference.Preference;
34 import android.preference.PreferenceGroup;
35 import android.preference.PreferenceScreen;
36 import android.provider.SearchIndexableResource;
37 import android.provider.Settings;
38 import android.telephony.CarrierConfigManager;
39 import android.text.TextUtils;
40 import android.text.format.DateFormat;
41 import android.util.Log;
42 import android.widget.Toast;
43
44 import com.android.internal.logging.MetricsLogger;
45 import com.android.settings.search.BaseSearchIndexProvider;
46 import com.android.settings.search.Index;
47 import com.android.settings.search.Indexable;
48
49 import java.io.BufferedReader;
50 import java.io.FileReader;
51 import java.io.IOException;
52 import java.text.ParseException;
53 import java.text.SimpleDateFormat;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.Date;
57 import java.util.List;
58 import java.util.Locale;
59 import java.util.regex.Matcher;
60 import java.util.regex.Pattern;
61
62 public class DeviceInfoSettings extends SettingsPreferenceFragment implements Indexable {
63
64     private static final String LOG_TAG = "DeviceInfoSettings";
65     private static final String FILENAME_PROC_VERSION = "/proc/version";
66     private static final String FILENAME_MSV = "/sys/board_properties/soc/msv";
67
68     private static final String KEY_MANUAL = "manual";
69     private static final String KEY_REGULATORY_INFO = "regulatory_info";
70     private static final String KEY_SYSTEM_UPDATE_SETTINGS = "system_update_settings";
71     private static final String PROPERTY_URL_SAFETYLEGAL = "ro.url.safetylegal";
72     private static final String PROPERTY_SELINUX_STATUS = "ro.build.selinux";
73     private static final String KEY_KERNEL_VERSION = "kernel_version";
74     private static final String KEY_BUILD_NUMBER = "build_number";
75     private static final String KEY_DEVICE_MODEL = "device_model";
76     private static final String KEY_SELINUX_STATUS = "selinux_status";
77     private static final String KEY_BASEBAND_VERSION = "baseband_version";
78     private static final String KEY_FIRMWARE_VERSION = "firmware_version";
79     private static final String KEY_SECURITY_PATCH = "security_patch";
80     private static final String KEY_UPDATE_SETTING = "additional_system_update_settings";
81     private static final String KEY_EQUIPMENT_ID = "fcc_equipment_id";
82     private static final String PROPERTY_EQUIPMENT_ID = "ro.ril.fccid";
83     private static final String KEY_DEVICE_FEEDBACK = "device_feedback";
84     private static final String KEY_SAFETY_LEGAL = "safetylegal";
85
86     static final int TAPS_TO_BE_A_DEVELOPER = 7;
87
88     long[] mHits = new long[3];
89     int mDevHitCountdown;
90     Toast mDevHitToast;
91
92     @Override
93     protected int getMetricsCategory() {
94         return MetricsLogger.DEVICEINFO;
95     }
96
97     @Override
98     protected int getHelpResource() {
99         return R.string.help_uri_about;
100     }
101
102     @Override
103     public void onCreate(Bundle icicle) {
104         super.onCreate(icicle);
105
106         addPreferencesFromResource(R.xml.device_info_settings);
107
108         setStringSummary(KEY_FIRMWARE_VERSION, Build.VERSION.RELEASE);
109         findPreference(KEY_FIRMWARE_VERSION).setEnabled(true);
110         String patch = Build.VERSION.SECURITY_PATCH;
111         if (!"".equals(patch)) {
112             try {
113                 SimpleDateFormat template = new SimpleDateFormat("yyyy-MM-dd");
114                 Date patchDate = template.parse(patch);
115                 String format = DateFormat.getBestDateTimePattern(Locale.getDefault(), "dMMMMyyyy");
116                 patch = DateFormat.format(format, patchDate).toString();
117             } catch (ParseException e) {
118                 // broken parse; fall through and use the raw string
119             }
120             setStringSummary(KEY_SECURITY_PATCH, patch);
121         } else {
122             getPreferenceScreen().removePreference(findPreference(KEY_SECURITY_PATCH));
123
124         }
125         setValueSummary(KEY_BASEBAND_VERSION, "gsm.version.baseband");
126         setStringSummary(KEY_DEVICE_MODEL, Build.MODEL + getMsvSuffix());
127         setValueSummary(KEY_EQUIPMENT_ID, PROPERTY_EQUIPMENT_ID);
128         setStringSummary(KEY_DEVICE_MODEL, Build.MODEL);
129         setStringSummary(KEY_BUILD_NUMBER, Build.DISPLAY);
130         findPreference(KEY_BUILD_NUMBER).setEnabled(true);
131         findPreference(KEY_KERNEL_VERSION).setSummary(getFormattedKernelVersion());
132
133         if (!SELinux.isSELinuxEnabled()) {
134             String status = getResources().getString(R.string.selinux_status_disabled);
135             setStringSummary(KEY_SELINUX_STATUS, status);
136         } else if (!SELinux.isSELinuxEnforced()) {
137             String status = getResources().getString(R.string.selinux_status_permissive);
138             setStringSummary(KEY_SELINUX_STATUS, status);
139         }
140
141         // Remove selinux information if property is not present
142         removePreferenceIfPropertyMissing(getPreferenceScreen(), KEY_SELINUX_STATUS,
143                 PROPERTY_SELINUX_STATUS);
144
145         // Remove Safety information preference if PROPERTY_URL_SAFETYLEGAL is not set
146         removePreferenceIfPropertyMissing(getPreferenceScreen(), KEY_SAFETY_LEGAL,
147                 PROPERTY_URL_SAFETYLEGAL);
148
149         // Remove Equipment id preference if FCC ID is not set by RIL
150         removePreferenceIfPropertyMissing(getPreferenceScreen(), KEY_EQUIPMENT_ID,
151                 PROPERTY_EQUIPMENT_ID);
152
153         // Remove Baseband version if wifi-only device
154         if (Utils.isWifiOnly(getActivity())) {
155             getPreferenceScreen().removePreference(findPreference(KEY_BASEBAND_VERSION));
156         }
157
158         // Dont show feedback option if there is no reporter.
159         if (TextUtils.isEmpty(getFeedbackReporterPackage(getActivity()))) {
160             getPreferenceScreen().removePreference(findPreference(KEY_DEVICE_FEEDBACK));
161         }
162
163         /*
164          * Settings is a generic app and should not contain any device-specific
165          * info.
166          */
167         final Activity act = getActivity();
168
169         // These are contained by the root preference screen
170         PreferenceGroup parentPreference = getPreferenceScreen();
171         if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
172             Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference,
173                     KEY_SYSTEM_UPDATE_SETTINGS,
174                     Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
175         } else {
176             // Remove for secondary users
177             removePreference(KEY_SYSTEM_UPDATE_SETTINGS);
178         }
179
180         // Read platform settings for additional system update setting
181         removePreferenceIfBoolFalse(KEY_UPDATE_SETTING,
182                 R.bool.config_additional_system_update_setting_enable);
183
184         // Remove manual entry if none present.
185         removePreferenceIfBoolFalse(KEY_MANUAL, R.bool.config_show_manual);
186
187         // Remove regulatory information if none present.
188         final Intent intent = new Intent(Settings.ACTION_SHOW_REGULATORY_INFO);
189         if (getPackageManager().queryIntentActivities(intent, 0).isEmpty()) {
190             Preference pref = findPreference(KEY_REGULATORY_INFO);
191             if (pref != null) {
192                 getPreferenceScreen().removePreference(pref);
193             }
194         }
195     }
196
197     @Override
198     public void onResume() {
199         super.onResume();
200         mDevHitCountdown = getActivity().getSharedPreferences(DevelopmentSettings.PREF_FILE,
201                 Context.MODE_PRIVATE).getBoolean(DevelopmentSettings.PREF_SHOW,
202                         android.os.Build.TYPE.equals("eng")) ? -1 : TAPS_TO_BE_A_DEVELOPER;
203         mDevHitToast = null;
204     }
205
206     @Override
207     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
208         if (preference.getKey().equals(KEY_FIRMWARE_VERSION)) {
209             System.arraycopy(mHits, 1, mHits, 0, mHits.length-1);
210             mHits[mHits.length-1] = SystemClock.uptimeMillis();
211             if (mHits[0] >= (SystemClock.uptimeMillis()-500)) {
212                 UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
213                 if (um.hasUserRestriction(UserManager.DISALLOW_FUN)) {
214                     Log.d(LOG_TAG, "Sorry, no fun for you!");
215                     return false;
216                 }
217
218                 Intent intent = new Intent(Intent.ACTION_MAIN);
219                 intent.setClassName("android",
220                         com.android.internal.app.PlatLogoActivity.class.getName());
221                 try {
222                     startActivity(intent);
223                 } catch (Exception e) {
224                     Log.e(LOG_TAG, "Unable to start activity " + intent.toString());
225                 }
226             }
227         } else if (preference.getKey().equals(KEY_BUILD_NUMBER)) {
228             // Don't enable developer options for secondary users.
229             if (UserHandle.myUserId() != UserHandle.USER_OWNER) return true;
230
231             // Don't enable developer options until device has been provisioned
232             if (Settings.Global.getInt(getActivity().getContentResolver(),
233                     Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
234                 return true;
235             }
236
237             final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
238             if (um.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES)) return true;
239
240             if (mDevHitCountdown > 0) {
241                 mDevHitCountdown--;
242                 if (mDevHitCountdown == 0) {
243                     getActivity().getSharedPreferences(DevelopmentSettings.PREF_FILE,
244                             Context.MODE_PRIVATE).edit().putBoolean(
245                                     DevelopmentSettings.PREF_SHOW, true).apply();
246                     if (mDevHitToast != null) {
247                         mDevHitToast.cancel();
248                     }
249                     mDevHitToast = Toast.makeText(getActivity(), R.string.show_dev_on,
250                             Toast.LENGTH_LONG);
251                     mDevHitToast.show();
252                     // This is good time to index the Developer Options
253                     Index.getInstance(
254                             getActivity().getApplicationContext()).updateFromClassNameResource(
255                                     DevelopmentSettings.class.getName(), true, true);
256
257                 } else if (mDevHitCountdown > 0
258                         && mDevHitCountdown < (TAPS_TO_BE_A_DEVELOPER-2)) {
259                     if (mDevHitToast != null) {
260                         mDevHitToast.cancel();
261                     }
262                     mDevHitToast = Toast.makeText(getActivity(), getResources().getQuantityString(
263                             R.plurals.show_dev_countdown, mDevHitCountdown, mDevHitCountdown),
264                             Toast.LENGTH_SHORT);
265                     mDevHitToast.show();
266                 }
267             } else if (mDevHitCountdown < 0) {
268                 if (mDevHitToast != null) {
269                     mDevHitToast.cancel();
270                 }
271                 mDevHitToast = Toast.makeText(getActivity(), R.string.show_dev_already,
272                         Toast.LENGTH_LONG);
273                 mDevHitToast.show();
274             }
275         } else if (preference.getKey().equals(KEY_DEVICE_FEEDBACK)) {
276             sendFeedback();
277         } else if(preference.getKey().equals(KEY_SYSTEM_UPDATE_SETTINGS)) {
278             CarrierConfigManager configManager =
279                     (CarrierConfigManager) getSystemService(Context.CARRIER_CONFIG_SERVICE);
280             PersistableBundle b = configManager.getConfig();
281             if (b.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) {
282                 ciActionOnSysUpdate(b);
283             }
284         }
285         return super.onPreferenceTreeClick(preferenceScreen, preference);
286     }
287
288     /**
289      * Trigger client initiated action (send intent) on system update
290      */
291     private void ciActionOnSysUpdate(PersistableBundle b) {
292         String intentStr = b.getString(CarrierConfigManager.
293                 KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING);
294         if (!TextUtils.isEmpty(intentStr)) {
295             String extra = b.getString(CarrierConfigManager.
296                     KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING);
297             String extraVal = b.getString(CarrierConfigManager.
298                     KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING);
299
300             Intent intent = new Intent(intentStr);
301             if (!TextUtils.isEmpty(extra)) {
302                 intent.putExtra(extra, extraVal);
303             }
304             Log.d(LOG_TAG, "ciActionOnSysUpdate: broadcasting intent " + intentStr +
305                     " with extra " + extra + ", " + extraVal);
306             getActivity().getApplicationContext().sendBroadcast(intent);
307         }
308     }
309
310     private void removePreferenceIfPropertyMissing(PreferenceGroup preferenceGroup,
311             String preference, String property ) {
312         if (SystemProperties.get(property).equals("")) {
313             // Property is missing so remove preference from group
314             try {
315                 preferenceGroup.removePreference(findPreference(preference));
316             } catch (RuntimeException e) {
317                 Log.d(LOG_TAG, "Property '" + property + "' missing and no '"
318                         + preference + "' preference");
319             }
320         }
321     }
322
323     private void removePreferenceIfBoolFalse(String preference, int resId) {
324         if (!getResources().getBoolean(resId)) {
325             Preference pref = findPreference(preference);
326             if (pref != null) {
327                 getPreferenceScreen().removePreference(pref);
328             }
329         }
330     }
331
332     private void setStringSummary(String preference, String value) {
333         try {
334             findPreference(preference).setSummary(value);
335         } catch (RuntimeException e) {
336             findPreference(preference).setSummary(
337                 getResources().getString(R.string.device_info_default));
338         }
339     }
340
341     private void setValueSummary(String preference, String property) {
342         try {
343             findPreference(preference).setSummary(
344                     SystemProperties.get(property,
345                             getResources().getString(R.string.device_info_default)));
346         } catch (RuntimeException e) {
347             // No recovery
348         }
349     }
350
351     private void sendFeedback() {
352         String reporterPackage = getFeedbackReporterPackage(getActivity());
353         if (TextUtils.isEmpty(reporterPackage)) {
354             return;
355         }
356         Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
357         intent.setPackage(reporterPackage);
358         startActivityForResult(intent, 0);
359     }
360
361     /**
362      * Reads a line from the specified file.
363      * @param filename the file to read from
364      * @return the first line, if any.
365      * @throws IOException if the file couldn't be read
366      */
367     private static String readLine(String filename) throws IOException {
368         BufferedReader reader = new BufferedReader(new FileReader(filename), 256);
369         try {
370             return reader.readLine();
371         } finally {
372             reader.close();
373         }
374     }
375
376     public static String getFormattedKernelVersion() {
377         try {
378             return formatKernelVersion(readLine(FILENAME_PROC_VERSION));
379
380         } catch (IOException e) {
381             Log.e(LOG_TAG,
382                 "IO Exception when getting kernel version for Device Info screen",
383                 e);
384
385             return "Unavailable";
386         }
387     }
388
389     public static String formatKernelVersion(String rawKernelVersion) {
390         // Example (see tests for more):
391         // Linux version 3.0.31-g6fb96c9 (android-build@xxx.xxx.xxx.xxx.com) \
392         //     (gcc version 4.6.x-xxx 20120106 (prerelease) (GCC) ) #1 SMP PREEMPT \
393         //     Thu Jun 28 11:02:39 PDT 2012
394
395         final String PROC_VERSION_REGEX =
396             "Linux version (\\S+) " + /* group 1: "3.0.31-g6fb96c9" */
397             "\\((\\S+?)\\) " +        /* group 2: "x@y.com" (kernel builder) */
398             "(?:\\(gcc.+? \\)) " +    /* ignore: GCC version information */
399             "(#\\d+) " +              /* group 3: "#1" */
400             "(?:.*?)?" +              /* ignore: optional SMP, PREEMPT, and any CONFIG_FLAGS */
401             "((Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)"; /* group 4: "Thu Jun 28 11:02:39 PDT 2012" */
402
403         Matcher m = Pattern.compile(PROC_VERSION_REGEX).matcher(rawKernelVersion);
404         if (!m.matches()) {
405             Log.e(LOG_TAG, "Regex did not match on /proc/version: " + rawKernelVersion);
406             return "Unavailable";
407         } else if (m.groupCount() < 4) {
408             Log.e(LOG_TAG, "Regex match on /proc/version only returned " + m.groupCount()
409                     + " groups");
410             return "Unavailable";
411         }
412         return m.group(1) + "\n" +                 // 3.0.31-g6fb96c9
413             m.group(2) + " " + m.group(3) + "\n" + // x@y.com #1
414             m.group(4);                            // Thu Jun 28 11:02:39 PDT 2012
415     }
416
417     /**
418      * Returns " (ENGINEERING)" if the msv file has a zero value, else returns "".
419      * @return a string to append to the model number description.
420      */
421     private String getMsvSuffix() {
422         // Production devices should have a non-zero value. If we can't read it, assume it's a
423         // production device so that we don't accidentally show that it's an ENGINEERING device.
424         try {
425             String msv = readLine(FILENAME_MSV);
426             // Parse as a hex number. If it evaluates to a zero, then it's an engineering build.
427             if (Long.parseLong(msv, 16) == 0) {
428                 return " (ENGINEERING)";
429             }
430         } catch (IOException ioe) {
431             // Fail quietly, as the file may not exist on some devices.
432         } catch (NumberFormatException nfe) {
433             // Fail quietly, returning empty string should be sufficient
434         }
435         return "";
436     }
437
438     private static String getFeedbackReporterPackage(Context context) {
439         final String feedbackReporter =
440                 context.getResources().getString(R.string.oem_preferred_feedback_reporter);
441         if (TextUtils.isEmpty(feedbackReporter)) {
442             // Reporter not configured. Return.
443             return feedbackReporter;
444         }
445         // Additional checks to ensure the reporter is on system image, and reporter is
446         // configured to listen to the intent. Otherwise, dont show the "send feedback" option.
447         final Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
448
449         PackageManager pm = context.getPackageManager();
450         List<ResolveInfo> resolvedPackages =
451                 pm.queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER);
452         for (ResolveInfo info : resolvedPackages) {
453             if (info.activityInfo != null) {
454                 if (!TextUtils.isEmpty(info.activityInfo.packageName)) {
455                     try {
456                         ApplicationInfo ai = pm.getApplicationInfo(info.activityInfo.packageName, 0);
457                         if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
458                             // Package is on the system image
459                             if (TextUtils.equals(
460                                         info.activityInfo.packageName, feedbackReporter)) {
461                                 return feedbackReporter;
462                             }
463                         }
464                     } catch (PackageManager.NameNotFoundException e) {
465                          // No need to do anything here.
466                     }
467                 }
468             }
469         }
470         return null;
471     }
472
473     /**
474      * For Search.
475      */
476     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
477         new BaseSearchIndexProvider() {
478
479             @Override
480             public List<SearchIndexableResource> getXmlResourcesToIndex(
481                     Context context, boolean enabled) {
482                 final SearchIndexableResource sir = new SearchIndexableResource(context);
483                 sir.xmlResId = R.xml.device_info_settings;
484                 return Arrays.asList(sir);
485             }
486
487             @Override
488             public List<String> getNonIndexableKeys(Context context) {
489                 final List<String> keys = new ArrayList<String>();
490                 if (isPropertyMissing(PROPERTY_SELINUX_STATUS)) {
491                     keys.add(KEY_SELINUX_STATUS);
492                 }
493                 if (isPropertyMissing(PROPERTY_URL_SAFETYLEGAL)) {
494                     keys.add(KEY_SAFETY_LEGAL);
495                 }
496                 if (isPropertyMissing(PROPERTY_EQUIPMENT_ID)) {
497                     keys.add(KEY_EQUIPMENT_ID);
498                 }
499                 // Remove Baseband version if wifi-only device
500                 if (Utils.isWifiOnly(context)) {
501                     keys.add((KEY_BASEBAND_VERSION));
502                 }
503                 // Dont show feedback option if there is no reporter.
504                 if (TextUtils.isEmpty(getFeedbackReporterPackage(context))) {
505                     keys.add(KEY_DEVICE_FEEDBACK);
506                 }
507                 if (UserHandle.myUserId() != UserHandle.USER_OWNER) {
508                     keys.add(KEY_SYSTEM_UPDATE_SETTINGS);
509                 }
510                 if (!context.getResources().getBoolean(
511                         R.bool.config_additional_system_update_setting_enable)) {
512                     keys.add(KEY_UPDATE_SETTING);
513                 }
514                 return keys;
515             }
516
517             private boolean isPropertyMissing(String property) {
518                 return SystemProperties.get(property).equals("");
519             }
520         };
521
522 }
523