Home | History | Annotate | Download | only in development
      1 /*
      2  * Copyright (C) 2017 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.settingslib.development;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.os.Bundle;
     24 import android.os.SystemProperties;
     25 import android.text.TextUtils;
     26 
     27 import androidx.annotation.VisibleForTesting;
     28 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
     29 import androidx.preference.ListPreference;
     30 import androidx.preference.Preference;
     31 import androidx.preference.PreferenceScreen;
     32 
     33 import com.android.settingslib.R;
     34 import com.android.settingslib.core.ConfirmationDialogController;
     35 import com.android.settingslib.core.lifecycle.Lifecycle;
     36 import com.android.settingslib.core.lifecycle.LifecycleObserver;
     37 import com.android.settingslib.core.lifecycle.events.OnCreate;
     38 import com.android.settingslib.core.lifecycle.events.OnDestroy;
     39 
     40 public abstract class AbstractLogpersistPreferenceController extends
     41         DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
     42         LifecycleObserver, OnCreate, OnDestroy, ConfirmationDialogController {
     43 
     44     private static final String SELECT_LOGPERSIST_KEY = "select_logpersist";
     45     private static final String SELECT_LOGPERSIST_PROPERTY = "persist.logd.logpersistd";
     46     @VisibleForTesting
     47     static final String ACTUAL_LOGPERSIST_PROPERTY = "logd.logpersistd";
     48     @VisibleForTesting
     49     static final String SELECT_LOGPERSIST_PROPERTY_SERVICE = "logcatd";
     50     private static final String SELECT_LOGPERSIST_PROPERTY_CLEAR = "clear";
     51     private static final String SELECT_LOGPERSIST_PROPERTY_STOP = "stop";
     52     private static final String SELECT_LOGPERSIST_PROPERTY_BUFFER =
     53             "persist.logd.logpersistd.buffer";
     54     @VisibleForTesting
     55     static final String ACTUAL_LOGPERSIST_PROPERTY_BUFFER = "logd.logpersistd.buffer";
     56     private static final String ACTUAL_LOGPERSIST_PROPERTY_ENABLE = "logd.logpersistd.enable";
     57 
     58     private ListPreference mLogpersist;
     59     private boolean mLogpersistCleared;
     60 
     61     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
     62         @Override
     63         public void onReceive(Context context, Intent intent) {
     64             final String currentValue = intent.getStringExtra(
     65                     AbstractLogdSizePreferenceController.EXTRA_CURRENT_LOGD_VALUE);
     66             onLogdSizeSettingUpdate(currentValue);
     67         }
     68     };
     69 
     70     public AbstractLogpersistPreferenceController(Context context, Lifecycle lifecycle) {
     71         super(context);
     72         if (isAvailable() && lifecycle != null) {
     73             lifecycle.addObserver(this);
     74         }
     75     }
     76 
     77     @Override
     78     public boolean isAvailable() {
     79         return TextUtils.equals(SystemProperties.get("ro.debuggable", "0"), "1");
     80     }
     81 
     82     @Override
     83     public String getPreferenceKey() {
     84         return SELECT_LOGPERSIST_KEY;
     85     }
     86 
     87     @Override
     88     public void displayPreference(PreferenceScreen screen) {
     89         super.displayPreference(screen);
     90         if (isAvailable()) {
     91             mLogpersist = (ListPreference) screen.findPreference(SELECT_LOGPERSIST_KEY);
     92         }
     93     }
     94 
     95     @Override
     96     public boolean onPreferenceChange(Preference preference, Object newValue) {
     97         if (preference == mLogpersist) {
     98             writeLogpersistOption(newValue, false);
     99             return true;
    100         } else {
    101             return false;
    102         }
    103     }
    104 
    105     @Override
    106     public void onCreate(Bundle savedInstanceState) {
    107         LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver,
    108                 new IntentFilter(AbstractLogdSizePreferenceController.ACTION_LOGD_SIZE_UPDATED));
    109     }
    110 
    111     @Override
    112     public void onDestroy() {
    113         LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver);
    114     }
    115 
    116     public void enablePreference(boolean enabled) {
    117         if (isAvailable()) {
    118             mLogpersist.setEnabled(enabled);
    119         }
    120     }
    121 
    122     private void onLogdSizeSettingUpdate(String currentValue) {
    123         if (mLogpersist != null) {
    124             String currentLogpersistEnable
    125                     = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY_ENABLE);
    126             if ((currentLogpersistEnable == null)
    127                     || !currentLogpersistEnable.equals("true")
    128                     || currentValue.equals(
    129                     AbstractLogdSizePreferenceController.SELECT_LOGD_OFF_SIZE_MARKER_VALUE)) {
    130                 writeLogpersistOption(null, true);
    131                 mLogpersist.setEnabled(false);
    132             } else if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
    133                 mLogpersist.setEnabled(true);
    134             }
    135         }
    136     }
    137 
    138     public void updateLogpersistValues() {
    139         if (mLogpersist == null) {
    140             return;
    141         }
    142         String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
    143         if (currentValue == null) {
    144             currentValue = "";
    145         }
    146         String currentBuffers = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY_BUFFER);
    147         if ((currentBuffers == null) || (currentBuffers.length() == 0)) {
    148             currentBuffers = "all";
    149         }
    150         int index = 0;
    151         if (currentValue.equals(SELECT_LOGPERSIST_PROPERTY_SERVICE)) {
    152             index = 1;
    153             if (currentBuffers.equals("kernel")) {
    154                 index = 3;
    155             } else if (!currentBuffers.equals("all") &&
    156                     !currentBuffers.contains("radio") &&
    157                     currentBuffers.contains("security") &&
    158                     currentBuffers.contains("kernel")) {
    159                 index = 2;
    160                 if (!currentBuffers.contains("default")) {
    161                     String[] contains = {"main", "events", "system", "crash"};
    162                     for (String type : contains) {
    163                         if (!currentBuffers.contains(type)) {
    164                             index = 1;
    165                             break;
    166                         }
    167                     }
    168                 }
    169             }
    170         }
    171         mLogpersist.setValue(
    172                 mContext.getResources().getStringArray(R.array.select_logpersist_values)[index]);
    173         mLogpersist.setSummary(
    174                 mContext.getResources().getStringArray(R.array.select_logpersist_summaries)[index]);
    175         if (index != 0) {
    176             mLogpersistCleared = false;
    177         } else if (!mLogpersistCleared) {
    178             // would File.delete() directly but need to switch uid/gid to access
    179             SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY, SELECT_LOGPERSIST_PROPERTY_CLEAR);
    180             SystemPropPoker.getInstance().poke();
    181             mLogpersistCleared = true;
    182         }
    183     }
    184 
    185     protected void setLogpersistOff(boolean update) {
    186         SystemProperties.set(SELECT_LOGPERSIST_PROPERTY_BUFFER, "");
    187         // deal with trampoline of empty properties
    188         SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY_BUFFER, "");
    189         SystemProperties.set(SELECT_LOGPERSIST_PROPERTY, "");
    190         SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY,
    191                 update ? "" : SELECT_LOGPERSIST_PROPERTY_STOP);
    192         SystemPropPoker.getInstance().poke();
    193         if (update) {
    194             updateLogpersistValues();
    195         } else {
    196             for (int i = 0; i < 3; i++) {
    197                 String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
    198                 if ((currentValue == null) || currentValue.equals("")) {
    199                     break;
    200                 }
    201                 try {
    202                     Thread.sleep(100);
    203                 } catch (InterruptedException e) {
    204                     // Ignore
    205                 }
    206             }
    207         }
    208     }
    209 
    210     public void writeLogpersistOption(Object newValue, boolean skipWarning) {
    211         if (mLogpersist == null) {
    212             return;
    213         }
    214         String currentTag = SystemProperties.get(
    215                 AbstractLogdSizePreferenceController.SELECT_LOGD_TAG_PROPERTY);
    216         if ((currentTag != null) && currentTag.startsWith(
    217                 AbstractLogdSizePreferenceController.SELECT_LOGD_TAG_SILENCE)) {
    218             newValue = null;
    219             skipWarning = true;
    220         }
    221 
    222         if ((newValue == null) || newValue.toString().equals("")) {
    223             if (skipWarning) {
    224                 mLogpersistCleared = false;
    225             } else if (!mLogpersistCleared) {
    226                 // if transitioning from on to off, pop up an are you sure?
    227                 String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
    228                 if ((currentValue != null) &&
    229                         currentValue.equals(SELECT_LOGPERSIST_PROPERTY_SERVICE)) {
    230                     showConfirmationDialog(mLogpersist);
    231                     return;
    232                 }
    233             }
    234             setLogpersistOff(true);
    235             return;
    236         }
    237 
    238         String currentBuffer = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY_BUFFER);
    239         if ((currentBuffer != null) && !currentBuffer.equals(newValue.toString())) {
    240             setLogpersistOff(false);
    241         }
    242         SystemProperties.set(SELECT_LOGPERSIST_PROPERTY_BUFFER, newValue.toString());
    243         SystemProperties.set(SELECT_LOGPERSIST_PROPERTY, SELECT_LOGPERSIST_PROPERTY_SERVICE);
    244         SystemPropPoker.getInstance().poke();
    245         for (int i = 0; i < 3; i++) {
    246             String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
    247             if ((currentValue != null)
    248                     && currentValue.equals(SELECT_LOGPERSIST_PROPERTY_SERVICE)) {
    249                 break;
    250             }
    251             try {
    252                 Thread.sleep(100);
    253             } catch (InterruptedException e) {
    254                 // Ignore
    255             }
    256         }
    257         updateLogpersistValues();
    258     }
    259 }
    260