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