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