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 AbstractLogpersistPreferenceController(Context context, Lifecycle lifecycle)70 public AbstractLogpersistPreferenceController(Context context, Lifecycle lifecycle) { 71 super(context); 72 if (isAvailable() && lifecycle != null) { 73 lifecycle.addObserver(this); 74 } 75 } 76 77 @Override isAvailable()78 public boolean isAvailable() { 79 return TextUtils.equals(SystemProperties.get("ro.debuggable", "0"), "1"); 80 } 81 82 @Override getPreferenceKey()83 public String getPreferenceKey() { 84 return SELECT_LOGPERSIST_KEY; 85 } 86 87 @Override displayPreference(PreferenceScreen screen)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 onPreferenceChange(Preference preference, Object newValue)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 onCreate(Bundle savedInstanceState)106 public void onCreate(Bundle savedInstanceState) { 107 LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver, 108 new IntentFilter(AbstractLogdSizePreferenceController.ACTION_LOGD_SIZE_UPDATED)); 109 } 110 111 @Override onDestroy()112 public void onDestroy() { 113 LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver); 114 } 115 enablePreference(boolean enabled)116 public void enablePreference(boolean enabled) { 117 if (isAvailable()) { 118 mLogpersist.setEnabled(enabled); 119 } 120 } 121 onLogdSizeSettingUpdate(String currentValue)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 updateLogpersistValues()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 setLogpersistOff(boolean update)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 writeLogpersistOption(Object newValue, boolean skipWarning)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