diff options
45 files changed, 2238 insertions, 390 deletions
diff --git a/api/current.txt b/api/current.txt index 7eee77ed4e1b..321135bb825b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -30968,6 +30968,7 @@ package android.os { ctor public Build.VERSION(); field public static final java.lang.String BASE_OS; field public static final java.lang.String CODENAME; + field public static final int FIRST_SDK_INT; field public static final java.lang.String INCREMENTAL; field public static final int MIN_SUPPORTED_TARGET_SDK_INT; field public static final int PREVIEW_SDK_INT; @@ -40236,6 +40237,7 @@ package android.telephony { field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool"; field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool"; field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool"; + field public static final java.lang.String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = "show_signal_strength_in_sim_status_bool"; field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; diff --git a/api/test-current.txt b/api/test-current.txt index 27e585d0a828..3fc5cd6efc76 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -120,6 +120,7 @@ package android.app.usage { public class StorageStatsManager { method public boolean isQuotaSupported(java.util.UUID); + method public boolean isReservedSupported(java.util.UUID); } } diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 337aeaaf37ab..ef7d31b22d32 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -126,7 +126,7 @@ LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \ LOCAL_MODULE_CLASS := EXECUTABLES -#LOCAL_INIT_RC := statsd.rc +LOCAL_INIT_RC := statsd.rc include $(BUILD_EXECUTABLE) diff --git a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml index 1e28f6730709..2a254df2302a 100644 --- a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml +++ b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml @@ -160,13 +160,6 @@ android:layout_width="1dp" android:layout_height="30dp"/> - <Button - android:id="@+id/display_output" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/display_output" - android:textSize="30dp"/> - <Space android:layout_width="1dp" android:layout_height="30dp"/> diff --git a/cmds/statsd/tools/loadtest/res/raw/loadtest_config b/cmds/statsd/tools/loadtest/res/raw/loadtest_config Binary files differindex 78223674285d..fbce0e870e57 100755 --- a/cmds/statsd/tools/loadtest/res/raw/loadtest_config +++ b/cmds/statsd/tools/loadtest/res/raw/loadtest_config diff --git a/cmds/statsd/tools/loadtest/res/values/strings.xml b/cmds/statsd/tools/loadtest/res/values/strings.xml index cb38298873f0..522337ee656d 100644 --- a/cmds/statsd/tools/loadtest/res/values/strings.xml +++ b/cmds/statsd/tools/loadtest/res/values/strings.xml @@ -20,7 +20,6 @@ <string name="app_name">Statsd Loadtest</string> <string name="bucket_label">bucket size (mins): </string> <string name="burst_label">burst: </string> - <string name="display_output">Show metrics data</string> <string name="placebo">placebo</string> <string name="period_label">logging period (secs): </string> <string name="replication_label">metric replication: </string> diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java index 522dea61d188..a72f72e60faf 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java @@ -15,6 +15,7 @@ */ package com.android.statsd.loadtest; +import android.annotation.Nullable; import android.app.Activity; import android.app.AlarmManager; import android.app.PendingIntent; @@ -43,6 +44,10 @@ import android.widget.CheckBox; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import com.android.os.StatsLog.ConfigMetricsReport; +import com.android.os.StatsLog.ConfigMetricsReportList; +import com.android.os.StatsLog.StatsdStatsReport; +import java.util.List; /** * Runs a load test for statsd. @@ -191,13 +196,6 @@ public class LoadtestActivity extends Activity { } }); - findViewById(R.id.display_output).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - fetchAndDisplayData(); - } - }); - mAlarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE); mStatsManager = (StatsManager) getSystemService("stats"); mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); @@ -239,6 +237,48 @@ public class LoadtestActivity extends Activity { super.onDestroy(); } + @Nullable + public StatsdStatsReport getMetadata() { + if (!statsdRunning()) { + return null; + } + if (mStatsManager != null) { + byte[] data = mStatsManager.getMetadata(); + if (data != null) { + StatsdStatsReport report = null; + boolean good = false; + try { + return StatsdStatsReport.parseFrom(data); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + Log.d(TAG, "Bad StatsdStatsReport"); + } + } + } + return null; + } + + @Nullable + public List<ConfigMetricsReport> getData() { + if (!statsdRunning()) { + return null; + } + if (mStatsManager != null) { + byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_NAME); + if (data != null) { + ConfigMetricsReportList reports = null; + try { + reports = ConfigMetricsReportList.parseFrom(data); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + Log.d(TAG, "Invalid data"); + } + if (reports != null) { + return reports.getReportsList(); + } + } + } + return null; + } + private void onPerfAlarm() { if (mPerfData != null) { mPerfData.onAlarm(this); @@ -285,6 +325,9 @@ public class LoadtestActivity extends Activity { // Prepare to push a sequence of atoms to logd. mPusher = new SequencePusher(mBurst, mPlacebo); + // Force a data flush by requesting data. + getData(); + // Create a config and push it to statsd. if (!setConfig(mFactory.getConfig(mReplication, mBucketMins * 60 * 1000, mPlacebo))) { return; @@ -355,42 +398,6 @@ public class LoadtestActivity extends Activity { mPlaceboCheckBox.setEnabled(!mStarted); } - private void fetchAndDisplayData() { - if (!statsdRunning()) { - return; - } - if (mStatsManager != null) { - byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_NAME); - if (data != null) { - displayData(data); - } else { - mReportText.setText("Failed to pull data"); - } - } - } - - private void displayData(byte[] data) { - com.android.os.StatsLog.ConfigMetricsReportList reports = null; - boolean good = false; - if (data != null) { - try { - reports = com.android.os.StatsLog.ConfigMetricsReportList.parseFrom(data); - good = true; - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - // display it in the text view. - } - } - int size = data == null ? 0 : data.length; - StringBuilder sb = new StringBuilder(); - sb.append(good ? "Proto parsing OK!" : "Proto parsing Error!"); - sb.append(" size:").append(size).append("\n"); - - if (good && reports != null) { - DisplayProtoUtils.displayLogReport(sb, reports); - mReportText.setText(sb.toString()); - } - } - private boolean statsdRunning() { if (IStatsManager.Stub.asInterface(ServiceManager.getService("stats")) == null) { Log.d(TAG, "Statsd not running"); diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java index d82a0eadea65..d9513a15208a 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java @@ -39,7 +39,6 @@ public class MemoryDataRecorder extends PerfDataRecorder { @Override public void onAlarm(Context context) { - Log.d(TAG, "GOT ALARM IN MEM"); runDumpsysStats(context, DUMP_FILENAME, "meminfo"); readDumpData(context, DUMP_FILENAME, new MemInfoParser(mStartTimeMillis), mSb); } diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java index 81a84f53b503..555e6dd2d99d 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java @@ -22,7 +22,7 @@ import android.widget.TextView; public abstract class NumericalWatcher implements TextWatcher { - private static final String TAG = "loadtest.NumericalWatcher"; + private static final String TAG = "loadtest.NumericalWatcher"; private final TextView mTextView; private final int mMin; @@ -45,9 +45,6 @@ public abstract class NumericalWatcher implements TextWatcher { } int unsanitized = Integer.parseInt(s); int newValue = sanitize(unsanitized); - - Log.d(TAG, "YOYO " + currentValue + " " + newValue + " " + unsanitized); - if (currentValue != newValue || unsanitized != newValue) { currentValue = newValue; editable.clear(); diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java index 466524756c48..22ba9c5aca39 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java @@ -51,13 +51,17 @@ public class PerfData extends PerfDataRecorder { private final Set<PerfDataRecorder> mRecorders; - public PerfData(Context context, boolean placebo, int replication, long bucketMins, - long periodSecs, int burst) { + public PerfData(LoadtestActivity loadtestActivity, boolean placebo, int replication, + long bucketMins, long periodSecs, int burst) { super(placebo, replication, bucketMins, periodSecs, burst); mRecorders = new HashSet(); mRecorders.add(new BatteryDataRecorder(placebo, replication, bucketMins, periodSecs, burst)); mRecorders.add(new MemoryDataRecorder(placebo, replication, bucketMins, periodSecs, burst)); - mAlarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + mRecorders.add(new StatsdStatsRecorder(loadtestActivity, placebo, replication, bucketMins, + periodSecs, burst)); + mRecorders.add(new ValidationRecorder(loadtestActivity, placebo, replication, bucketMins, + periodSecs, burst)); + mAlarmMgr = (AlarmManager) loadtestActivity.getSystemService(Context.ALARM_SERVICE); } public void onDestroy() { diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java index 15a8e5c87131..5b5ba3766d04 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java @@ -35,12 +35,12 @@ import java.util.Date; public abstract class PerfDataRecorder { private static final String TAG = "loadtest.PerfDataRecorder"; - protected final String mFileSuffix; + protected final String mTimeAsString; protected final String mColumnSuffix; protected PerfDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs, int burst) { - mFileSuffix = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date()); + mTimeAsString = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date()); mColumnSuffix = getColumnSuffix(placebo, replication, bucketMins, periodSecs, burst); } @@ -103,7 +103,7 @@ public abstract class PerfDataRecorder { /** Writes CSV data to a file. */ protected void writeData(Context context, String filePrefix, String columnPrefix, StringBuilder sb) { - File dataFile = new File(getStorageDir(), filePrefix + mFileSuffix + ".csv"); + File dataFile = new File(getStorageDir(), filePrefix + mTimeAsString + ".csv"); FileWriter writer = null; try { @@ -131,7 +131,7 @@ public abstract class PerfDataRecorder { private File getStorageDir() { File file = new File(Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DOCUMENTS), "loadtest"); + Environment.DIRECTORY_DOCUMENTS), "loadtest/" + mTimeAsString); if (!file.mkdirs()) { Log.e(TAG, "Directory not created"); } diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java new file mode 100644 index 000000000000..4ef5dc2f6ca8 --- /dev/null +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.statsd.loadtest; + +import android.content.Context; +import android.util.Log; +import com.android.os.StatsLog.StatsdStatsReport; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StatsdStatsRecorder extends PerfDataRecorder { + private static final String TAG = "loadtest.StatsdStatsRecorder"; + + private final LoadtestActivity mLoadtestActivity; + + public StatsdStatsRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication, + long bucketMins, long periodSecs, int burst) { + super(placebo, replication, bucketMins, periodSecs, burst); + mLoadtestActivity = loadtestActivity; + } + + @Override + public void startRecording(Context context) { + // Nothing to do. + } + + @Override + public void onAlarm(Context context) { + // Nothing to do. + } + + @Override + public void stopRecording(Context context) { + StatsdStatsReport metadata = mLoadtestActivity.getMetadata(); + if (metadata != null) { + int numConfigs = metadata.getConfigStatsCount(); + StringBuilder sb = new StringBuilder(); + StatsdStatsReport.ConfigStats configStats = metadata.getConfigStats(numConfigs - 1); + sb.append("metric_count,") + .append(configStats.getMetricCount() + "\n") + .append("condition_count,") + .append(configStats.getConditionCount() + "\n") + .append("matcher_count,") + .append(configStats.getMatcherCount() + "\n"); + writeData(context, "statsdstats_", "", sb); + } + } +} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java new file mode 100644 index 000000000000..4b614aa19492 --- /dev/null +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.statsd.loadtest; + +import android.content.Context; +import android.util.Log; +import com.android.os.StatsLog.ConfigMetricsReport; +import com.android.os.StatsLog.EventMetricData; +import com.android.os.StatsLog.StatsLogReport; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Checks the correctness of the stats. + */ +public class ValidationRecorder extends PerfDataRecorder { + private static final String TAG = "loadtest.ValidationRecorder"; + + private final LoadtestActivity mLoadtestActivity; + + public ValidationRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication, + long bucketMins, long periodSecs, int burst) { + super(placebo, replication, bucketMins, periodSecs, burst); + mLoadtestActivity = loadtestActivity; + } + + @Override + public void startRecording(Context context) { + // Nothing to do. + } + + @Override + public void onAlarm(Context context) { + validateData(); + } + + @Override + public void stopRecording(Context context) { + validateData(); + } + + private void validateData() { + List<ConfigMetricsReport> reports = mLoadtestActivity.getData(); + if (reports != null) { + Log.d(TAG, "GOT DATA"); + for (ConfigMetricsReport report : reports) { + for (StatsLogReport logReport : report.getMetricsList()) { + if (!logReport.hasMetricName()) { + Log.e(TAG, "Metric missing name."); + continue; + } + String metricName = logReport.getMetricName(); + if (metricName.startsWith("EVENT_BATTERY_LEVEL_CHANGES_WHILE_SCREEN_IS_ON_")) { + validateEventBatteryLevelChangesWhileScreenIsOn(logReport); + continue; + } + if (metricName.startsWith("EVENT_BATTERY_LEVEL_CHANGES_")) { + validateEventBatteryLevelChanges(logReport); + continue; + } + } + } + } + } + + private void validateEventBatteryLevelChanges(StatsLogReport logReport) { + Log.d(TAG, "Validating " + logReport.getMetricName()); + if (logReport.hasEventMetrics()) { + Log.d(TAG, "Num events captured: " + logReport.getEventMetrics().getDataCount()); + for (EventMetricData data : logReport.getEventMetrics().getDataList()) { + Log.d(TAG, " Event : " + data.getAtom()); + } + } else { + Log.d(TAG, "Metric is invalid"); + } + } + + private void validateEventBatteryLevelChangesWhileScreenIsOn(StatsLogReport logReport) { + Log.d(TAG, "Validating " + logReport.getMetricName()); + } +} diff --git a/core/java/android/app/usage/IStorageStatsManager.aidl b/core/java/android/app/usage/IStorageStatsManager.aidl index 15e5ea5f44ff..7eacc8996bb9 100644 --- a/core/java/android/app/usage/IStorageStatsManager.aidl +++ b/core/java/android/app/usage/IStorageStatsManager.aidl @@ -22,6 +22,7 @@ import android.app.usage.ExternalStorageStats; /** {@hide} */ interface IStorageStatsManager { boolean isQuotaSupported(String volumeUuid, String callingPackage); + boolean isReservedSupported(String volumeUuid, String callingPackage); long getTotalBytes(String volumeUuid, String callingPackage); long getFreeBytes(String volumeUuid, String callingPackage); long getCacheBytes(String volumeUuid, String callingPackage); diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java index 3d187ec7cb50..a86c27a03358 100644 --- a/core/java/android/app/usage/StorageStatsManager.java +++ b/core/java/android/app/usage/StorageStatsManager.java @@ -78,6 +78,16 @@ public class StorageStatsManager { return isQuotaSupported(convert(uuid)); } + /** {@hide} */ + @TestApi + public boolean isReservedSupported(@NonNull UUID storageUuid) { + try { + return mService.isReservedSupported(convert(storageUuid), mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Return the total size of the underlying physical media that is hosting * this storage volume. diff --git a/core/java/android/hardware/display/BrightnessConfiguration.aidl b/core/java/android/hardware/display/BrightnessConfiguration.aidl new file mode 100644 index 000000000000..5b6b4645fa45 --- /dev/null +++ b/core/java/android/hardware/display/BrightnessConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +parcelable BrightnessConfiguration; diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java new file mode 100644 index 000000000000..6c3be816e87b --- /dev/null +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Pair; + +import com.android.internal.util.Preconditions; + +import java.util.Arrays; + +/** @hide */ +public final class BrightnessConfiguration implements Parcelable { + private final float[] mLux; + private final float[] mNits; + + private BrightnessConfiguration(float[] lux, float[] nits) { + mLux = lux; + mNits = nits; + } + + /** + * Gets the base brightness as curve. + * + * The curve is returned as a pair of float arrays, the first representing all of the lux + * points of the brightness curve and the second representing all of the nits values of the + * brightness curve. + * + * @return the control points for the brightness curve. + */ + public Pair<float[], float[]> getCurve() { + return Pair.create(Arrays.copyOf(mLux, mLux.length), Arrays.copyOf(mNits, mNits.length)); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloatArray(mLux); + dest.writeFloatArray(mNits); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("BrightnessConfiguration{["); + final int size = mLux.length; + for (int i = 0; i < size; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")"); + } + sb.append("]}"); + return sb.toString(); + } + + @Override + public int hashCode() { + int result = 1; + result = result * 31 + Arrays.hashCode(mLux); + result = result * 31 + Arrays.hashCode(mNits); + return result; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof BrightnessConfiguration)) { + return false; + } + final BrightnessConfiguration other = (BrightnessConfiguration) o; + return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits); + } + + public static final Creator<BrightnessConfiguration> CREATOR = + new Creator<BrightnessConfiguration>() { + public BrightnessConfiguration createFromParcel(Parcel in) { + Builder builder = new Builder(); + float[] lux = in.createFloatArray(); + float[] nits = in.createFloatArray(); + builder.setCurve(lux, nits); + return builder.build(); + } + + public BrightnessConfiguration[] newArray(int size) { + return new BrightnessConfiguration[size]; + } + }; + + /** + * A builder class for {@link BrightnessConfiguration}s. + */ + public static class Builder { + private float[] mCurveLux; + private float[] mCurveNits; + + /** + * Sets the control points for the brightness curve. + * + * Brightness curves must have strictly increasing ambient brightness values in lux and + * monotonically increasing display brightness values in nits. In addition, the initial + * control point must be 0 lux. + * + * @throws IllegalArgumentException if the initial control point is not at 0 lux. + * @throws IllegalArgumentException if the lux levels are not strictly increasing. + * @throws IllegalArgumentException if the nit levels are not monotonically increasing. + */ + public Builder setCurve(float[] lux, float[] nits) { + Preconditions.checkNotNull(lux); + Preconditions.checkNotNull(nits); + if (lux.length == 0 || nits.length == 0) { + throw new IllegalArgumentException("Lux and nits arrays must not be empty"); + } + if (lux.length != nits.length) { + throw new IllegalArgumentException("Lux and nits arrays must be the same length"); + } + if (lux[0] != 0) { + throw new IllegalArgumentException("Initial control point must be for 0 lux"); + } + Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux"); + Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits"); + checkMonotonic(lux, true/*strictly increasing*/, "lux"); + checkMonotonic(nits, false /*strictly increasing*/, "nits"); + mCurveLux = lux; + mCurveNits = nits; + return this; + } + + /** + * Builds the {@link BrightnessConfiguration}. + * + * A brightness curve <b>must</b> be set before calling this. + */ + public BrightnessConfiguration build() { + if (mCurveLux == null || mCurveNits == null) { + throw new IllegalStateException("A curve must be set!"); + } + return new BrightnessConfiguration(mCurveLux, mCurveNits); + } + + private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) { + if (vals.length <= 1) { + return; + } + float prev = vals[0]; + for (int i = 1; i < vals.length; i++) { + if (prev > vals[i] || prev == vals[i] && strictlyIncreasing) { + String condition = strictlyIncreasing ? "strictly increasing" : "monotonic"; + throw new IllegalArgumentException(name + " values must be " + condition); + } + prev = vals[i]; + } + } + } +} diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 97ca231bf68c..7de667dcaa2b 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -27,6 +27,7 @@ import android.content.Context; import android.graphics.Point; import android.media.projection.MediaProjection; import android.os.Handler; +import android.os.UserHandle; import android.util.SparseArray; import android.view.Display; import android.view.Surface; @@ -634,6 +635,27 @@ public final class DisplayManager { } /** + * Sets the global display brightness configuration. + * + * @hide + */ + public void setBrightnessConfiguration(BrightnessConfiguration c) { + setBrightnessConfigurationForUser(c, UserHandle.myUserId()); + } + + /** + * Sets the global display brightness configuration for a specific user. + * + * Note this requires the INTERACT_ACROSS_USERS permission if setting the configuration for a + * user other than the one you're currently running as. + * + * @hide + */ + public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId) { + mGlobal.setBrightnessConfigurationForUser(c, userId); + } + + /** * Listens for changes in available display devices. */ public interface DisplayListener { diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index c3f82f56dad8..bf4cc1d826a9 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -487,6 +487,19 @@ public final class DisplayManagerGlobal { } } + /** + * Sets the global brightness configuration for a given user. + * + * @hide + */ + public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId) { + try { + mDm.setBrightnessConfigurationForUser(c, userId); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { @Override public void onDisplayEvent(int displayId, int event) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index f2ed9e7571d3..8afae6ec9010 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -18,6 +18,7 @@ package android.hardware.display; import android.content.pm.ParceledListSlice; import android.graphics.Point; +import android.hardware.display.BrightnessConfiguration; import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.WifiDisplay; @@ -89,4 +90,9 @@ interface IDisplayManager { // STOPSHIP remove when adaptive brightness code is updated to accept curves. // Requires BRIGHTNESS_SLIDER_USAGE permission. void setBrightness(int brightness); + + // Sets the global brightness configuration for a given user. Requires + // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user being configured is not + // the same as the calling user. + void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId); } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 32cbcdc664bd..48f56847e88d 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -221,13 +221,31 @@ public class Build { public static final String SDK = getString("ro.build.version.sdk"); /** - * The user-visible SDK version of the framework; its possible - * values are defined in {@link Build.VERSION_CODES}. + * The SDK version of the software currently running on this hardware + * device. This value never changes while a device is booted, but it may + * increase when the hardware manufacturer provides an OTA update. + * <p> + * Possible values are defined in {@link Build.VERSION_CODES}. + * + * @see #FIRST_SDK_INT */ public static final int SDK_INT = SystemProperties.getInt( "ro.build.version.sdk", 0); /** + * The SDK version of the software that <em>initially</em> shipped on + * this hardware device. It <em>never</em> changes during the lifetime + * of the device, even when {@link #SDK_INT} increases due to an OTA + * update. + * <p> + * Possible values are defined in {@link Build.VERSION_CODES}. + * + * @see #SDK_INT + */ + public static final int FIRST_SDK_INT = SystemProperties + .getInt("ro.product.first_api_level", 0); + + /** * The developer preview revision of a prerelease SDK. This value will always * be <code>0</code> on production platform builds/devices. * diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index 52e53b074165..bc2953e0c0fe 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -520,162 +520,163 @@ public class GestureDetector { boolean handled = false; switch (action & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_POINTER_DOWN: - mDownFocusX = mLastFocusX = focusX; - mDownFocusY = mLastFocusY = focusY; - // Cancel long press and taps - cancelTaps(); - break; - - case MotionEvent.ACTION_POINTER_UP: - mDownFocusX = mLastFocusX = focusX; - mDownFocusY = mLastFocusY = focusY; - - // Check the dot product of current velocities. - // If the pointer that left was opposing another velocity vector, clear. - mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); - final int upIndex = ev.getActionIndex(); - final int id1 = ev.getPointerId(upIndex); - final float x1 = mVelocityTracker.getXVelocity(id1); - final float y1 = mVelocityTracker.getYVelocity(id1); - for (int i = 0; i < count; i++) { - if (i == upIndex) continue; - - final int id2 = ev.getPointerId(i); - final float x = x1 * mVelocityTracker.getXVelocity(id2); - final float y = y1 * mVelocityTracker.getYVelocity(id2); - - final float dot = x + y; - if (dot < 0) { - mVelocityTracker.clear(); - break; + case MotionEvent.ACTION_POINTER_DOWN: + mDownFocusX = mLastFocusX = focusX; + mDownFocusY = mLastFocusY = focusY; + // Cancel long press and taps + cancelTaps(); + break; + + case MotionEvent.ACTION_POINTER_UP: + mDownFocusX = mLastFocusX = focusX; + mDownFocusY = mLastFocusY = focusY; + + // Check the dot product of current velocities. + // If the pointer that left was opposing another velocity vector, clear. + mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); + final int upIndex = ev.getActionIndex(); + final int id1 = ev.getPointerId(upIndex); + final float x1 = mVelocityTracker.getXVelocity(id1); + final float y1 = mVelocityTracker.getYVelocity(id1); + for (int i = 0; i < count; i++) { + if (i == upIndex) continue; + + final int id2 = ev.getPointerId(i); + final float x = x1 * mVelocityTracker.getXVelocity(id2); + final float y = y1 * mVelocityTracker.getYVelocity(id2); + + final float dot = x + y; + if (dot < 0) { + mVelocityTracker.clear(); + break; + } } - } - break; - - case MotionEvent.ACTION_DOWN: - if (mDoubleTapListener != null) { - boolean hadTapMessage = mHandler.hasMessages(TAP); - if (hadTapMessage) mHandler.removeMessages(TAP); - if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage && - isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { - // This is a second tap - mIsDoubleTapping = true; - // Give a callback with the first tap of the double-tap - handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent); - // Give a callback with down event of the double-tap - handled |= mDoubleTapListener.onDoubleTapEvent(ev); - } else { - // This is a first tap - mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); + break; + + case MotionEvent.ACTION_DOWN: + if (mDoubleTapListener != null) { + boolean hadTapMessage = mHandler.hasMessages(TAP); + if (hadTapMessage) mHandler.removeMessages(TAP); + if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) + && hadTapMessage + && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { + // This is a second tap + mIsDoubleTapping = true; + // Give a callback with the first tap of the double-tap + handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent); + // Give a callback with down event of the double-tap + handled |= mDoubleTapListener.onDoubleTapEvent(ev); + } else { + // This is a first tap + mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); + } } - } - mDownFocusX = mLastFocusX = focusX; - mDownFocusY = mLastFocusY = focusY; - if (mCurrentDownEvent != null) { - mCurrentDownEvent.recycle(); - } - mCurrentDownEvent = MotionEvent.obtain(ev); - mAlwaysInTapRegion = true; - mAlwaysInBiggerTapRegion = true; - mStillDown = true; - mInLongPress = false; - mDeferConfirmSingleTap = false; - - if (mIsLongpressEnabled) { - mHandler.removeMessages(LONG_PRESS); - mHandler.sendEmptyMessageAtTime(LONG_PRESS, - mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT); - } - mHandler.sendEmptyMessageAtTime(SHOW_PRESS, - mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); - handled |= mListener.onDown(ev); - break; + mDownFocusX = mLastFocusX = focusX; + mDownFocusY = mLastFocusY = focusY; + if (mCurrentDownEvent != null) { + mCurrentDownEvent.recycle(); + } + mCurrentDownEvent = MotionEvent.obtain(ev); + mAlwaysInTapRegion = true; + mAlwaysInBiggerTapRegion = true; + mStillDown = true; + mInLongPress = false; + mDeferConfirmSingleTap = false; - case MotionEvent.ACTION_MOVE: - if (mInLongPress || mInContextClick) { + if (mIsLongpressEnabled) { + mHandler.removeMessages(LONG_PRESS); + mHandler.sendEmptyMessageAtTime(LONG_PRESS, + mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT); + } + mHandler.sendEmptyMessageAtTime(SHOW_PRESS, + mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); + handled |= mListener.onDown(ev); break; - } - final float scrollX = mLastFocusX - focusX; - final float scrollY = mLastFocusY - focusY; - if (mIsDoubleTapping) { - // Give the move events of the double-tap - handled |= mDoubleTapListener.onDoubleTapEvent(ev); - } else if (mAlwaysInTapRegion) { - final int deltaX = (int) (focusX - mDownFocusX); - final int deltaY = (int) (focusY - mDownFocusY); - int distance = (deltaX * deltaX) + (deltaY * deltaY); - int slopSquare = isGeneratedGesture ? 0 : mTouchSlopSquare; - if (distance > slopSquare) { + + case MotionEvent.ACTION_MOVE: + if (mInLongPress || mInContextClick) { + break; + } + final float scrollX = mLastFocusX - focusX; + final float scrollY = mLastFocusY - focusY; + if (mIsDoubleTapping) { + // Give the move events of the double-tap + handled |= mDoubleTapListener.onDoubleTapEvent(ev); + } else if (mAlwaysInTapRegion) { + final int deltaX = (int) (focusX - mDownFocusX); + final int deltaY = (int) (focusY - mDownFocusY); + int distance = (deltaX * deltaX) + (deltaY * deltaY); + int slopSquare = isGeneratedGesture ? 0 : mTouchSlopSquare; + if (distance > slopSquare) { + handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); + mLastFocusX = focusX; + mLastFocusY = focusY; + mAlwaysInTapRegion = false; + mHandler.removeMessages(TAP); + mHandler.removeMessages(SHOW_PRESS); + mHandler.removeMessages(LONG_PRESS); + } + int doubleTapSlopSquare = isGeneratedGesture ? 0 : mDoubleTapTouchSlopSquare; + if (distance > doubleTapSlopSquare) { + mAlwaysInBiggerTapRegion = false; + } + } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastFocusX = focusX; mLastFocusY = focusY; - mAlwaysInTapRegion = false; - mHandler.removeMessages(TAP); - mHandler.removeMessages(SHOW_PRESS); - mHandler.removeMessages(LONG_PRESS); } - int doubleTapSlopSquare = isGeneratedGesture ? 0 : mDoubleTapTouchSlopSquare; - if (distance > doubleTapSlopSquare) { - mAlwaysInBiggerTapRegion = false; + break; + + case MotionEvent.ACTION_UP: + mStillDown = false; + MotionEvent currentUpEvent = MotionEvent.obtain(ev); + if (mIsDoubleTapping) { + // Finally, give the up event of the double-tap + handled |= mDoubleTapListener.onDoubleTapEvent(ev); + } else if (mInLongPress) { + mHandler.removeMessages(TAP); + mInLongPress = false; + } else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) { + handled = mListener.onSingleTapUp(ev); + if (mDeferConfirmSingleTap && mDoubleTapListener != null) { + mDoubleTapListener.onSingleTapConfirmed(ev); + } + } else if (!mIgnoreNextUpEvent) { + + // A fling must travel the minimum tap distance + final VelocityTracker velocityTracker = mVelocityTracker; + final int pointerId = ev.getPointerId(0); + velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); + final float velocityY = velocityTracker.getYVelocity(pointerId); + final float velocityX = velocityTracker.getXVelocity(pointerId); + + if ((Math.abs(velocityY) > mMinimumFlingVelocity) + || (Math.abs(velocityX) > mMinimumFlingVelocity)) { + handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY); + } } - } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) { - handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); - mLastFocusX = focusX; - mLastFocusY = focusY; - } - break; - - case MotionEvent.ACTION_UP: - mStillDown = false; - MotionEvent currentUpEvent = MotionEvent.obtain(ev); - if (mIsDoubleTapping) { - // Finally, give the up event of the double-tap - handled |= mDoubleTapListener.onDoubleTapEvent(ev); - } else if (mInLongPress) { - mHandler.removeMessages(TAP); - mInLongPress = false; - } else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) { - handled = mListener.onSingleTapUp(ev); - if (mDeferConfirmSingleTap && mDoubleTapListener != null) { - mDoubleTapListener.onSingleTapConfirmed(ev); + if (mPreviousUpEvent != null) { + mPreviousUpEvent.recycle(); } - } else if (!mIgnoreNextUpEvent) { - - // A fling must travel the minimum tap distance - final VelocityTracker velocityTracker = mVelocityTracker; - final int pointerId = ev.getPointerId(0); - velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); - final float velocityY = velocityTracker.getYVelocity(pointerId); - final float velocityX = velocityTracker.getXVelocity(pointerId); - - if ((Math.abs(velocityY) > mMinimumFlingVelocity) - || (Math.abs(velocityX) > mMinimumFlingVelocity)){ - handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY); + // Hold the event we obtained above - listeners may have changed the original. + mPreviousUpEvent = currentUpEvent; + if (mVelocityTracker != null) { + // This may have been cleared when we called out to the + // application above. + mVelocityTracker.recycle(); + mVelocityTracker = null; } - } - if (mPreviousUpEvent != null) { - mPreviousUpEvent.recycle(); - } - // Hold the event we obtained above - listeners may have changed the original. - mPreviousUpEvent = currentUpEvent; - if (mVelocityTracker != null) { - // This may have been cleared when we called out to the - // application above. - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - mIsDoubleTapping = false; - mDeferConfirmSingleTap = false; - mIgnoreNextUpEvent = false; - mHandler.removeMessages(SHOW_PRESS); - mHandler.removeMessages(LONG_PRESS); - break; - - case MotionEvent.ACTION_CANCEL: - cancel(); - break; + mIsDoubleTapping = false; + mDeferConfirmSingleTap = false; + mIgnoreNextUpEvent = false; + mHandler.removeMessages(SHOW_PRESS); + mHandler.removeMessages(LONG_PRESS); + break; + + case MotionEvent.ACTION_CANCEL: + cancel(); + break; } if (!handled && mInputEventConsistencyVerifier != null) { diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index e5d571672ce2..91c76afdf5b2 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -494,4 +494,38 @@ public class Preconditions { return value; } + + /** + * Ensures that all elements in the argument integer array are within the inclusive range + * + * @param value an integer array of values + * @param lower the lower endpoint of the inclusive range + * @param upper the upper endpoint of the inclusive range + * @param valueName the name of the argument to use if the check fails + * + * @return the validated integer array + * + * @throws IllegalArgumentException if any of the elements in {@code value} were out of range + * @throws NullPointerException if the {@code value} was {@code null} + */ + public static int[] checkArrayElementsInRange(int[] value, int lower, int upper, + String valueName) { + checkNotNull(value, valueName + " must not be null"); + + for (int i = 0; i < value.length; ++i) { + int v = value[i]; + + if (v < lower) { + throw new IllegalArgumentException( + String.format("%s[%d] is out of range of [%d, %d] (too low)", + valueName, i, lower, upper)); + } else if (v > upper) { + throw new IllegalArgumentException( + String.format("%s[%d] is out of range of [%d, %d] (too high)", + valueName, i, lower, upper)); + } + } + + return value; + } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ab7b07ec96ba..15e439ed3586 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2924,6 +2924,11 @@ <permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to modify the display brightness configuration + @hide --> + <permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS" + android:protectionLevel="signature|privileged|development" /> + <!-- @SystemApi Allows an application to control VPN. <p>Not for use by third-party applications.</p> @hide --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9a26229d5946..8377751f4236 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1295,6 +1295,22 @@ <integer-array name="config_autoBrightnessLcdBacklightValues"> </integer-array> + <!-- Array of desired screen brightness in nits corresponding to the lux values + in the config_autoBrightnessLevels array. As with config_screenBrightnessMinimumNits and + config_screenBrightnessMaximumNits, the display brightness is defined as the measured + brightness of an all-white image. + + If this is defined then: + - config_autoBrightnessLcdBacklightValues should not be defined + - config_screenBrightnessMinimumNits must be defined + - config_screenBrightnessMaximumNits must be defined + + This array should have size one greater than the size of the config_autoBrightnessLevels + array. The brightness values must be non-negative and non-decreasing. This must be + overridden in platform specific overlays --> + <array name="config_autoBrightnessDisplayValuesNits"> + </array> + <!-- Array of output values for button backlight corresponding to the LUX values in the config_autoBrightnessLevels array. This array should have size one greater than the size of the config_autoBrightnessLevels array. @@ -1331,6 +1347,29 @@ <item>200</item> </integer-array> + <!-- The minimum brightness of the display in nits. On OLED displays this should be measured + with an all white image while the display is fully on and the backlight is set to + config_screenBrightnessSettingMinimum or config_screenBrightnessSettingDark, whichever + is darker. + + If this and config_screenBrightnessMinimumNits are set, then the display's brightness + range is assumed to be linear between + (config_screenBrightnessSettingMinimum, config_screenBrightnessMinimumNits) and + (config_screenBrightnessSettingMaximum, config_screenBrightnessMaximumNits). --> + <item name="config_screenBrightnessMinimumNits" format="float" type="dimen">-1.0</item> + + <!-- The maximum brightness of the display in nits. On OLED displays this should be measured + with an all white image while the display is fully on and the "backlight" is set to + config_screenBrightnessSettingMaximum. Note that this value should *not* reflect the + maximum brightness value for any high brightness modes but only the maximum brightness + value obtainable in a sustainable manner. + + If this and config_screenBrightnessMinimumNits are set to something non-negative, then the + display's brightness range is assumed to be linear between + (config_screenBrightnessSettingMinimum, config_screenBrightnessMaximumNits) and + (config_screenBrightnessSettingMaximum, config_screenBrightnessMaximumNits). --> + <item name="config_screenBrightnessMaximumNits" format="float" type="dimen">-1.0</item> + <!-- Array of ambient lux threshold values. This is used for determining hysteresis constraint values by calculating the index to use for lookup and then setting the constraint value to the corresponding value of the array. The new brightening hysteresis constraint value diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 69d27fc7059f..be7ce5fc7e57 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3185,4 +3185,8 @@ <java-symbol type="string" name="global_action_logout" /> <java-symbol type="drawable" name="ic_logout" /> + + <java-symbol type="dimen" name="config_screenBrightnessMinimumNits" /> + <java-symbol type="dimen" name="config_screenBrightnessMaximumNits" /> + <java-symbol type="array" name="config_autoBrightnessDisplayValuesNits" /> </resources> diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java new file mode 100644 index 000000000000..bad0d258a54c --- /dev/null +++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.os.Parcel; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.Pair; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BrightnessConfigurationTest { + private static final float[] LUX_LEVELS = { + 0f, + 10f, + 100f, + }; + + private static final float[] NITS_LEVELS = { + 0.5f, + 90f, + 100f, + }; + + @Test + public void testSetCurveIsUnmodified() { + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + builder.setCurve(LUX_LEVELS, NITS_LEVELS); + BrightnessConfiguration config = builder.build(); + Pair<float[], float[]> curve = config.getCurve(); + assertArrayEquals(LUX_LEVELS, curve.first, "lux"); + assertArrayEquals(NITS_LEVELS, curve.second, "nits"); + } + + @Test(expected = IllegalArgumentException.class) + public void testCurveMustHaveZeroLuxPoint() { + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); + lux[0] = 1f; + builder.setCurve(lux, NITS_LEVELS); + } + + @Test(expected = IllegalStateException.class) + public void testCurveMustBeSet() { + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + builder.build(); + } + + @Test(expected = NullPointerException.class) + public void testCurveMustNotHaveNullArrays() { + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + builder.setCurve(null, null); + } + + @Test(expected = IllegalArgumentException.class) + public void testCurveMustNotHaveEmptyArrays() { + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + builder.setCurve(new float[0], new float[0]); + } + + @Test + public void testCurveMustNotHaveArraysOfDifferentLengths() { + assertThrows(IllegalArgumentException.class, () -> { + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length + 1); + lux[lux.length - 1] = lux[lux.length - 2] + 1; + boolean exceptionThrown = false; + builder.setCurve(lux, NITS_LEVELS); + }); + + assertThrows(IllegalArgumentException.class, () -> { + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length + 1); + nits[nits.length - 1] = nits[nits.length - 2] + 1; + builder.setCurve(LUX_LEVELS, nits); + }); + } + + @Test + public void testCurvesMustNotContainNaN() { + assertThrows(IllegalArgumentException.class, () -> { + float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); + lux[lux.length - 1] = Float.NaN; + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + builder.setCurve(lux, NITS_LEVELS); + }); + + assertThrows(IllegalArgumentException.class, () -> { + float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length); + nits[nits.length - 1] = Float.NaN; + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + builder.setCurve(LUX_LEVELS, nits); + }); + } + + + @Test + public void testParceledConfigIsEquivalent() { + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + builder.setCurve(LUX_LEVELS, NITS_LEVELS); + BrightnessConfiguration config = builder.build(); + Parcel p = Parcel.obtain(); + p.writeParcelable(config, 0 /*flags*/); + p.setDataPosition(0); + BrightnessConfiguration newConfig = + p.readParcelable(BrightnessConfiguration.class.getClassLoader()); + assertEquals(config, newConfig); + } + + @Test + public void testEquals() { + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + builder.setCurve(LUX_LEVELS, NITS_LEVELS); + BrightnessConfiguration baseConfig = builder.build(); + + builder = new BrightnessConfiguration.Builder(); + builder.setCurve(LUX_LEVELS, NITS_LEVELS); + BrightnessConfiguration identicalConfig = builder.build(); + assertEquals(baseConfig, identicalConfig); + assertEquals("hashCodes must be equal for identical configs", + baseConfig.hashCode(), identicalConfig.hashCode()); + + float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); + lux[lux.length - 1] = lux[lux.length - 1] * 2; + builder = new BrightnessConfiguration.Builder(); + builder.setCurve(lux, NITS_LEVELS); + BrightnessConfiguration luxDifferConfig = builder.build(); + assertNotEquals(baseConfig, luxDifferConfig); + + float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length); + nits[nits.length - 1] = nits[nits.length - 1] * 2; + builder = new BrightnessConfiguration.Builder(); + builder.setCurve(LUX_LEVELS, nits); + BrightnessConfiguration nitsDifferConfig = builder.build(); + assertNotEquals(baseConfig, nitsDifferConfig); + } + + private static void assertArrayEquals(float[] expected, float[] actual, String name) { + assertEquals("Expected " + name + " arrays to be the same length!", + expected.length, actual.length); + for (int i = 0; i < expected.length; i++) { + assertEquals("Expected " + name + " arrays to be equivalent when value " + i + + "differs", expected[i], actual[i], 0.01 /*tolerance*/); + } + } + + private interface ExceptionRunnable { + void run() throws Exception; + } + + private static void assertThrows(Class<? extends Throwable> exceptionClass, + ExceptionRunnable r) { + try { + r.run(); + } catch (Throwable e) { + assertTrue("Expected exception type " + exceptionClass.getName() + " but got " + + e.getClass().getName(), exceptionClass.isAssignableFrom(e.getClass())); + return; + } + fail("Expected exception type " + exceptionClass.getName() + + ", but no exception was thrown"); + } +} diff --git a/libs/hwui/ProfileDataContainer.cpp b/libs/hwui/ProfileDataContainer.cpp index 8e0b4e2eef5b..38e0f0aa8d83 100644 --- a/libs/hwui/ProfileDataContainer.cpp +++ b/libs/hwui/ProfileDataContainer.cpp @@ -21,6 +21,7 @@ #include <cutils/ashmem.h> #include <log/log.h> +#include <errno.h> #include <sys/mman.h> namespace android { diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 5e7f9c6a40e5..e313e868022d 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -301,6 +301,9 @@ <!-- Enable the default volume dialog --> <bool name="enable_volume_ui">true</bool> + <!-- Enable the default volume level warning dialog --> + <bool name="enable_safety_warning">true</bool> + <!-- Whether to show operator name in the status bar --> <bool name="config_showOperatorNameInStatusBar">false</bool> diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java index 4dff9bd5c782..bc98140f5af5 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java @@ -134,6 +134,10 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna mController.setVolumePolicy(mVolumePolicy); } + void setEnableDialogs(boolean volumeUi, boolean safetyWarning) { + mController.setEnableDialogs(volumeUi, safetyWarning); + } + @Override public void onUserActivity() { final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 275402651079..212979004c89 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -103,6 +103,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final Vibrator mVibrator; private final boolean mHasVibrator; private boolean mShowA11yStream; + private boolean mShowVolumeDialog; + private boolean mShowSafetyWarning; private boolean mDestroyed; private VolumePolicy mVolumePolicy; @@ -282,6 +284,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget(); } + public void setEnableDialogs(boolean volumeUi, boolean safetyWarning) { + mShowVolumeDialog = volumeUi; + mShowSafetyWarning = safetyWarning; + } + public void vibrate() { if (mHasVibrator) { mVibrator.vibrate(VIBRATE_HINT_DURATION); @@ -311,7 +318,9 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } private void onShowSafetyWarningW(int flags) { - mCallbacks.onShowSafetyWarning(flags); + if (mShowSafetyWarning) { + mCallbacks.onShowSafetyWarning(flags); + } } private void onAccessibilityModeChanged(Boolean showA11yStream) { @@ -343,7 +352,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa && mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_ASLEEP && mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP && mStatusBar.isDeviceInteractive() - && (flags & AudioManager.FLAG_SHOW_UI) != 0; + && (flags & AudioManager.FLAG_SHOW_UI) != 0 + && mShowVolumeDialog; } boolean onVolumeChangedW(int stream, int flags) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java index 02969e483e97..6f65b081afc4 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java @@ -40,9 +40,13 @@ public class VolumeUI extends SystemUI { @Override public void start() { - mEnabled = mContext.getResources().getBoolean(R.bool.enable_volume_ui); + boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui); + boolean enableSafetyWarning = + mContext.getResources().getBoolean(R.bool.enable_safety_warning); + mEnabled = enableVolumeUi || enableSafetyWarning; if (!mEnabled) return; mVolumeComponent = new VolumeDialogComponent(this, mContext, null); + mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning); putComponent(VolumeComponent.class, getVolumeComponent()); setDefaultVolumeController(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java index 06568f70f16c..401fd6857e76 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java @@ -45,6 +45,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { mCallback = mock(VolumeDialogControllerImpl.C.class); mStatusBar = mock(StatusBar.class); mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar); + mVolumeController.setEnableDialogs(true, true); } @Test diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 9a6e609445a5..6c5bfc793330 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -24,6 +24,7 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.hardware.display.BrightnessConfiguration; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -34,7 +35,6 @@ import android.text.format.DateUtils; import android.util.EventLog; import android.util.MathUtils; import android.util.Slog; -import android.util.Spline; import android.util.TimeUtils; import java.io.PrintWriter; @@ -76,9 +76,8 @@ class AutomaticBrightnessController { // The light sensor, or null if not available or needed. private final Sensor mLightSensor; - // The auto-brightness spline adjustment. - // The brightness values have been scaled to a range of 0..1. - private final Spline mScreenAutoBrightnessSpline; + // The mapper to translate ambient lux to screen brightness in the range [0, 1.0]. + private final BrightnessMappingStrategy mBrightnessMapper; // The minimum and maximum screen brightnesses. private final int mScreenBrightnessRangeMinimum; @@ -186,7 +185,7 @@ class AutomaticBrightnessController { private float mBrightnessAdjustmentSampleOldGamma; public AutomaticBrightnessController(Callbacks callbacks, Looper looper, - SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime, + SensorManager sensorManager, BrightnessMappingStrategy mapper, int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, @@ -194,7 +193,7 @@ class AutomaticBrightnessController { HysteresisLevels dynamicHysteresis) { mCallbacks = callbacks; mSensorManager = sensorManager; - mScreenAutoBrightnessSpline = autoBrightnessSpline; + mBrightnessMapper = mapper; mScreenBrightnessRangeMinimum = brightnessMin; mScreenBrightnessRangeMaximum = brightnessMax; mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime; @@ -228,15 +227,16 @@ class AutomaticBrightnessController { return mScreenAutoBrightness; } - public void configure(boolean enable, float adjustment, boolean dozing, - boolean userInitiatedChange) { + public void configure(boolean enable, @Nullable BrightnessConfiguration configuration, + float adjustment, boolean dozing, boolean userInitiatedChange) { // While dozing, the application processor may be suspended which will prevent us from // receiving new information from the light sensor. On some devices, we may be able to // switch to a wake-up light sensor instead but for now we will simply disable the sensor // and hold onto the last computed screen auto brightness. We save the dozing flag for // debugging purposes. mDozing = dozing; - boolean changed = setLightSensorEnabled(enable && !dozing); + boolean changed = setBrightnessConfiguration(configuration); + changed |= setLightSensorEnabled(enable && !dozing); if (enable && !dozing && userInitiatedChange) { prepareBrightnessAdjustmentSample(); } @@ -246,10 +246,13 @@ class AutomaticBrightnessController { } } + public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) { + return mBrightnessMapper.setBrightnessConfiguration(configuration); + } + public void dump(PrintWriter pw) { pw.println(); pw.println("Automatic Brightness Controller Configuration:"); - pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline); pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum); pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum); pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig); @@ -274,9 +277,13 @@ class AutomaticBrightnessController { mInitialHorizonAmbientLightRingBuffer); pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness); pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment); - pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma=" + mScreenAutoBrightnessAdjustmentMaxGamma); + pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma=" + + mScreenAutoBrightnessAdjustmentMaxGamma); pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma); pw.println(" mDozing=" + mDozing); + + pw.println(); + mBrightnessMapper.dump(pw); } private boolean setLightSensorEnabled(boolean enable) { @@ -533,7 +540,7 @@ class AutomaticBrightnessController { return; } - float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux); + float value = mBrightnessMapper.getBrightness(mAmbientLux); float gamma = 1.0f; if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java new file mode 100644 index 000000000000..3b9d40fa0825 --- /dev/null +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.annotation.Nullable; +import android.hardware.display.BrightnessConfiguration; +import android.os.PowerManager; +import android.util.MathUtils; +import android.util.Pair; +import android.util.Slog; +import android.util.Spline; + +import com.android.internal.util.Preconditions; +import com.android.internal.annotations.VisibleForTesting; + +import java.io.PrintWriter; + +/** + * A utility to map from an ambient brightness to a display's "backlight" brightness based on the + * available display information and brightness configuration. + * + * Note that without a mapping from the nits to a display backlight level, any + * {@link BrightnessConfiguration}s that are set are just ignored. + */ +public abstract class BrightnessMappingStrategy { + private static final String TAG = "BrightnessMappingStrategy"; + private static final boolean DEBUG = false; + + @Nullable + public static BrightnessMappingStrategy create( + float[] luxLevels, int[] brightnessLevelsBacklight, float[] brightnessLevelsNits, + float[] nitsRange, int[] backlightRange) { + if (isValidMapping(nitsRange, backlightRange) + && isValidMapping(luxLevels, brightnessLevelsNits)) { + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + builder.setCurve(luxLevels, brightnessLevelsNits); + return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange); + } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) { + return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight); + } else { + return null; + } + } + + private static boolean isValidMapping(float[] x, float[] y) { + if (x == null || y == null || x.length == 0 || y.length == 0) { + return false; + } + if (x.length != y.length) { + return false; + } + final int N = x.length; + float prevX = x[0]; + float prevY = y[0]; + if (prevX < 0 || prevY < 0 || Float.isNaN(prevX) || Float.isNaN(prevY)) { + return false; + } + for (int i = 1; i < N; i++) { + if (prevX >= x[i] || prevY > y[i]) { + return false; + } + if (Float.isNaN(x[i]) || Float.isNaN(y[i])) { + return false; + } + prevX = x[i]; + prevY = y[i]; + } + return true; + } + + private static boolean isValidMapping(float[] x, int[] y) { + if (x == null || y == null || x.length == 0 || y.length == 0) { + return false; + } + if (x.length != y.length) { + return false; + } + final int N = x.length; + float prevX = x[0]; + int prevY = y[0]; + if (prevX < 0 || prevY < 0 || Float.isNaN(prevX)) { + return false; + } + for (int i = 1; i < N; i++) { + if (prevX >= x[i] || prevY > y[i]) { + return false; + } + if (Float.isNaN(x[i])) { + return false; + } + prevX = x[i]; + prevY = y[i]; + } + return true; + } + + /** + * Sets the {@link BrightnessConfiguration}. + * + * @param config The new configuration. If {@code null} is passed, the default configuration is + * used. + * @return Whether the brightness configuration has changed. + */ + public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config); + + /** + * Returns the desired brightness of the display based on the current ambient lux. + * + * The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max + * brightness and 0 is the display at minimum brightness. + * + * @param lux The current ambient brightness in lux. + * @return The desired brightness of the display compressed to the range [0, 1.0]. + */ + public abstract float getBrightness(float lux); + + public abstract void dump(PrintWriter pw); + + private static float normalizeAbsoluteBrightness(int brightness) { + brightness = MathUtils.constrain(brightness, + PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); + return (float) brightness / PowerManager.BRIGHTNESS_ON; + } + + + /** + * A {@link BrightnessMappingStrategy} that maps from ambient room brightness directly to the + * backlight of the display. + * + * Since we don't have information about the display's physical brightness, any brightness + * configurations that are set are just ignored. + */ + private static class SimpleMappingStrategy extends BrightnessMappingStrategy { + private final Spline mSpline; + + public SimpleMappingStrategy(float[] lux, int[] brightness) { + Preconditions.checkArgument(lux.length != 0 && brightness.length != 0, + "Lux and brightness arrays must not be empty!"); + Preconditions.checkArgument(lux.length == brightness.length, + "Lux and brightness arrays must be the same length!"); + Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux"); + Preconditions.checkArrayElementsInRange(brightness, + 0, Integer.MAX_VALUE, "brightness"); + + final int N = brightness.length; + float[] x = new float[N]; + float[] y = new float[N]; + for (int i = 0; i < N; i++) { + x[i] = lux[i]; + y[i] = normalizeAbsoluteBrightness(brightness[i]); + } + + mSpline = Spline.createSpline(x, y); + if (DEBUG) { + Slog.d(TAG, "Auto-brightness spline: " + mSpline); + for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) { + Slog.d(TAG, String.format(" %7.1f: %7.1f", v, mSpline.interpolate(v))); + } + } + } + + @Override + public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { + Slog.e(TAG, + "setBrightnessConfiguration called on device without display information."); + return false; + } + + @Override + public float getBrightness(float lux) { + return mSpline.interpolate(lux); + } + + @Override + public void dump(PrintWriter pw) { + pw.println("SimpleMappingStrategy"); + pw.println(" mSpline=" + mSpline); + } + } + + /** A {@link BrightnessMappingStrategy} that maps from ambient room brightness to the physical + * range of the display, rather than to the range of the backlight control (typically 0-255). + * + * By mapping through the physical brightness, the curve becomes portable across devices and + * gives us more resolution in the resulting mapping. + */ + @VisibleForTesting + static class PhysicalMappingStrategy extends BrightnessMappingStrategy { + // The current brightness configuration. + private BrightnessConfiguration mConfig; + + // A spline mapping from the current ambient light in lux to the desired display brightness + // in nits. + private Spline mBrightnessSpline; + + // A spline mapping from nits to the corresponding backlight value, normalized to the range + // [0, 1.0]. + private final Spline mBacklightSpline; + + // The default brightness configuration. + private final BrightnessConfiguration mDefaultConfig; + + public PhysicalMappingStrategy(BrightnessConfiguration config, + float[] nits, int[] backlight) { + Preconditions.checkArgument(nits.length != 0 && backlight.length != 0, + "Nits and backlight arrays must not be empty!"); + Preconditions.checkArgument(nits.length == backlight.length, + "Nits and backlight arrays must be the same length!"); + Preconditions.checkNotNull(config); + Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits"); + Preconditions.checkArrayElementsInRange(backlight, + PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON, "backlight"); + + // Setup the backlight spline + final int N = nits.length; + float[] x = new float[N]; + float[] y = new float[N]; + for (int i = 0; i < N; i++) { + x[i] = nits[i]; + y[i] = normalizeAbsoluteBrightness(backlight[i]); + } + + mBacklightSpline = Spline.createSpline(x, y); + if (DEBUG) { + Slog.d(TAG, "Backlight spline: " + mBacklightSpline); + for (float v = 1f; v < nits[nits.length - 1] * 1.25f; v *= 1.25f) { + Slog.d(TAG, String.format( + " %7.1f: %7.1f", v, mBacklightSpline.interpolate(v))); + } + } + + mDefaultConfig = config; + setBrightnessConfiguration(config); + } + + @Override + public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) { + if (config == null) { + config = mDefaultConfig; + } + if (config.equals(mConfig)) { + if (DEBUG) { + Slog.d(TAG, "Tried to set an identical brightness config, ignoring"); + } + return false; + } + + Pair<float[], float[]> curve = config.getCurve(); + mBrightnessSpline = Spline.createSpline(curve.first /*lux*/, curve.second /*nits*/); + if (DEBUG) { + Slog.d(TAG, "Brightness spline: " + mBrightnessSpline); + final float[] lux = curve.first; + for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) { + Slog.d(TAG, String.format( + " %7.1f: %7.1f", v, mBrightnessSpline.interpolate(v))); + } + } + mConfig = config; + return true; + } + + @Override + public float getBrightness(float lux) { + return mBacklightSpline.interpolate(mBrightnessSpline.interpolate(lux)); + } + + @Override + public void dump(PrintWriter pw) { + pw.println("PhysicalMappingStrategy"); + pw.println(" mConfig=" + mConfig); + pw.println(" mBrightnessSpline=" + mBrightnessSpline); + pw.println(" mBacklightSpline=" + mBacklightSpline); + } + } +} diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index c1bfa478ee89..9b97934cfc3b 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -29,6 +29,7 @@ import com.android.internal.util.IndentingPrintWriter; import android.Manifest; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; @@ -37,6 +38,7 @@ import android.content.res.Resources; import android.graphics.Point; import android.hardware.SensorManager; import android.hardware.display.BrightnessChangeEvent; +import android.hardware.display.BrightnessConfiguration; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayViewport; @@ -62,6 +64,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; +import android.os.UserManager; import android.text.TextUtils; import android.util.IntArray; import android.util.Slog; @@ -70,6 +73,7 @@ import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; +import com.android.internal.util.Preconditions; import com.android.server.AnimationThread; import com.android.server.DisplayThread; import com.android.server.LocalServices; @@ -145,6 +149,7 @@ public final class DisplayManagerService extends SystemService { private static final int MSG_REQUEST_TRAVERSAL = 4; private static final int MSG_UPDATE_VIEWPORT = 5; private static final int MSG_REGISTER_BRIGHTNESS_TRACKER = 6; + private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 7; private final Context mContext; private final DisplayManagerHandler mHandler; @@ -219,6 +224,9 @@ public final class DisplayManagerService extends SystemService { // The virtual display adapter, or null if not registered. private VirtualDisplayAdapter mVirtualDisplayAdapter; + // The User ID of the current user + private @UserIdInt int mCurrentUserId; + // The stable device screen height and width. These are not tied to a specific display, even // the default display, because they need to be stable over the course of the device's entire // life, even if the default display changes (e.g. a new monitor is plugged into a PC-like @@ -278,17 +286,18 @@ public final class DisplayManagerService extends SystemService { mDisplayAdapterListener = new DisplayAdapterListener(); mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger( - com.android.internal.R.integer.config_defaultDisplayDefaultColorMode); + com.android.internal.R.integer.config_defaultDisplayDefaultColorMode); PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting(); mBrightnessTracker = new BrightnessTracker(context, null); + mCurrentUserId = UserHandle.USER_SYSTEM; } public void setupSchedulerPolicies() { // android.display and android.anim is critical to user experience and we should make sure - // it is not in the default foregroup groups, add it to top-app to make sure it uses all the - // cores and scheduling settings for top-app when it runs. + // it is not in the default foregroup groups, add it to top-app to make sure it uses all + // the cores and scheduling settings for top-app when it runs. Process.setThreadGroupAndCpuset(DisplayThread.get().getThreadId(), Process.THREAD_GROUP_TOP_APP); Process.setThreadGroupAndCpuset(AnimationThread.get().getThreadId(), @@ -342,6 +351,19 @@ public final class DisplayManagerService extends SystemService { } } + @Override + public void onSwitchUser(@UserIdInt int newUserId) { + final int userSerial = getUserManager().getUserSerialNumber(newUserId); + synchronized (mSyncRoot) { + if (mCurrentUserId != newUserId) { + mCurrentUserId = newUserId; + BrightnessConfiguration config = + mPersistentDataStore.getBrightnessConfiguration(userSerial); + mDisplayPowerController.setBrightnessConfiguration(config); + } + } + } + // TODO: Use dependencies or a boot phase public void windowManagerAndInputReady() { synchronized (mSyncRoot) { @@ -985,6 +1007,30 @@ public final class DisplayManagerService extends SystemService { } } + private void setBrightnessConfigurationForUserInternal( + @NonNull BrightnessConfiguration c, @UserIdInt int userId) { + final int userSerial = getUserManager().getUserSerialNumber(userId); + synchronized (mSyncRoot) { + try { + mPersistentDataStore.setBrightnessConfigurationForUser(c, userSerial); + } finally { + mPersistentDataStore.saveIfNeeded(); + } + if (userId == mCurrentUserId) { + mDisplayPowerController.setBrightnessConfiguration(c); + } + } + } + + private void loadBrightnessConfiguration() { + synchronized (mSyncRoot) { + final int userSerial = getUserManager().getUserSerialNumber(mCurrentUserId); + BrightnessConfiguration config = + mPersistentDataStore.getBrightnessConfiguration(userSerial); + mDisplayPowerController.setBrightnessConfiguration(config); + } + } + // Updates all existing logical displays given the current set of display devices. // Removes invalid logical displays. // Sends notifications if needed. @@ -1229,6 +1275,10 @@ public final class DisplayManagerService extends SystemService { return mProjectionService; } + private UserManager getUserManager() { + return mContext.getSystemService(UserManager.class); + } + private void dumpInternal(PrintWriter pw) { pw.println("DISPLAY MANAGER (dumpsys display)"); @@ -1371,6 +1421,10 @@ public final class DisplayManagerService extends SystemService { case MSG_REGISTER_BRIGHTNESS_TRACKER: mBrightnessTracker.start(); break; + + case MSG_LOAD_BRIGHTNESS_CONFIGURATION: + loadBrightnessConfiguration(); + break; } } } @@ -1800,6 +1854,27 @@ public final class DisplayManagerService extends SystemService { } } + @Override // Binder call + public void setBrightnessConfigurationForUser( + BrightnessConfiguration c, @UserIdInt int userId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS, + "Permission required to change the display's brightness configuration"); + if (userId != UserHandle.getCallingUserId()) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.INTERACT_ACROSS_USERS, + "Permission required to change the display brightness" + + " configuration of another user"); + } + Preconditions.checkNotNull(c); + final long token = Binder.clearCallingIdentity(); + try { + setBrightnessConfigurationForUserInternal(c, userId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean validatePackageName(int uid, String packageName) { if (packageName != null) { String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid); @@ -1871,18 +1946,24 @@ public final class DisplayManagerService extends SystemService { mDisplayPowerController = new DisplayPowerController( mContext, callbacks, handler, sensorManager, blanker); } + + mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION); } @Override public boolean requestPowerState(DisplayPowerRequest request, boolean waitForNegativeProximity) { - return mDisplayPowerController.requestPowerState(request, - waitForNegativeProximity); + synchronized (mSyncRoot) { + return mDisplayPowerController.requestPowerState(request, + waitForNegativeProximity); + } } @Override public boolean isProximitySensorAvailable() { - return mDisplayPowerController.isProximitySensorAvailable(); + synchronized (mSyncRoot) { + return mDisplayPowerController.isProximitySensorAvailable(); + } } @Override diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 29a007a36d41..a2d954822e64 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -26,10 +26,12 @@ import android.animation.Animator; import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.hardware.display.BrightnessConfiguration; import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.Handler; @@ -93,6 +95,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2; private static final int MSG_SCREEN_ON_UNBLOCKED = 3; private static final int MSG_SCREEN_OFF_UNBLOCKED = 4; + private static final int MSG_CONFIGURE_BRIGHTNESS = 5; + private static final int MSG_USER_SWITCH = 6; private static final int PROXIMITY_UNKNOWN = -1; private static final int PROXIMITY_NEGATIVE = 0; @@ -285,6 +289,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The controller for the automatic brightness level. private AutomaticBrightnessController mAutomaticBrightnessController; + // The default brightness configuration. Used for whenever we don't have a valid brightness + // configuration set. This is typically seen with users that don't have a brightness + // configuration that's different from the default. + private BrightnessConfiguration mDefaultBrightnessConfiguration; + + // The current brightness configuration. + private BrightnessConfiguration mBrightnessConfiguration; + // Animators. private ObjectAnimator mColorFadeOnAnimator; private ObjectAnimator mColorFadeOffAnimator; @@ -333,7 +345,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call screenBrightnessSettingMinimum, mScreenBrightnessDimConfig), mScreenBrightnessDarkConfig); - mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON; + mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(resources.getInteger( + com.android.internal.R.integer.config_screenBrightnessSettingMaximum)); mUseSoftwareAutoBrightnessConfig = resources.getBoolean( com.android.internal.R.bool.config_automatic_brightness_available); @@ -348,60 +361,60 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mSkipScreenOnBrightnessRamp = resources.getBoolean( com.android.internal.R.bool.config_skipScreenOnBrightnessRamp); - int lightSensorRate = resources.getInteger( - com.android.internal.R.integer.config_autoBrightnessLightSensorRate); - int initialLightSensorRate = resources.getInteger( - com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate); - if (initialLightSensorRate == -1) { - initialLightSensorRate = lightSensorRate; - } else if (initialLightSensorRate > lightSensorRate) { - Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate (" - + initialLightSensorRate + ") to be less than or equal to " - + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ")."); - } - long brighteningLightDebounce = resources.getInteger( - com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce); - long darkeningLightDebounce = resources.getInteger( - com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce); - boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean( - com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp); - int ambientLightHorizon = resources.getInteger( - com.android.internal.R.integer.config_autoBrightnessAmbientLightHorizon); - float autoBrightnessAdjustmentMaxGamma = resources.getFraction( - com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, - 1, 1); - - int[] brightLevels = resources.getIntArray( - com.android.internal.R.array.config_dynamicHysteresisBrightLevels); - int[] darkLevels = resources.getIntArray( - com.android.internal.R.array.config_dynamicHysteresisDarkLevels); - int[] luxLevels = resources.getIntArray( - com.android.internal.R.array.config_dynamicHysteresisLuxLevels); - HysteresisLevels dynamicHysteresis = new HysteresisLevels( - brightLevels, darkLevels, luxLevels); - if (mUseSoftwareAutoBrightnessConfig) { - int[] lux = resources.getIntArray( - com.android.internal.R.array.config_autoBrightnessLevels); - int[] screenBrightness = resources.getIntArray( + float[] luxLevels = getLuxLevels(resources.getIntArray( + com.android.internal.R.array.config_autoBrightnessLevels)); + int[] backlightValues = resources.getIntArray( com.android.internal.R.array.config_autoBrightnessLcdBacklightValues); - int lightSensorWarmUpTimeConfig = resources.getInteger( - com.android.internal.R.integer.config_lightSensorWarmupTime); + float[] brightnessValuesNits = getFloatArray(resources.obtainTypedArray( + com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)); + + final float screenMinimumNits = resources.getFloat( + com.android.internal.R.dimen.config_screenBrightnessMinimumNits); + final float screenMaximumNits = resources.getFloat( + com.android.internal.R.dimen.config_screenBrightnessMaximumNits); + final float dozeScaleFactor = resources.getFraction( com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor, 1, 1); - Spline screenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness); - if (screenAutoBrightnessSpline == null) { - Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues " - + "(size " + screenBrightness.length + ") " - + "must be monotic and have exactly one more entry than " - + "config_autoBrightnessLevels (size " + lux.length + ") " - + "which must be strictly increasing. " - + "Auto-brightness will be disabled."); - mUseSoftwareAutoBrightnessConfig = false; - } else { - int bottom = clampAbsoluteBrightness(screenBrightness[0]); + int[] brightLevels = resources.getIntArray( + com.android.internal.R.array.config_dynamicHysteresisBrightLevels); + int[] darkLevels = resources.getIntArray( + com.android.internal.R.array.config_dynamicHysteresisDarkLevels); + int[] luxHysteresisLevels = resources.getIntArray( + com.android.internal.R.array.config_dynamicHysteresisLuxLevels); + HysteresisLevels dynamicHysteresis = new HysteresisLevels( + brightLevels, darkLevels, luxHysteresisLevels); + + long brighteningLightDebounce = resources.getInteger( + com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce); + long darkeningLightDebounce = resources.getInteger( + com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce); + boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean( + com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp); + int ambientLightHorizon = resources.getInteger( + com.android.internal.R.integer.config_autoBrightnessAmbientLightHorizon); + float autoBrightnessAdjustmentMaxGamma = resources.getFraction( + com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, + 1, 1); + + int lightSensorWarmUpTimeConfig = resources.getInteger( + com.android.internal.R.integer.config_lightSensorWarmupTime); + int lightSensorRate = resources.getInteger( + com.android.internal.R.integer.config_autoBrightnessLightSensorRate); + int initialLightSensorRate = resources.getInteger( + com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate); + if (initialLightSensorRate == -1) { + initialLightSensorRate = lightSensorRate; + } else if (initialLightSensorRate > lightSensorRate) { + Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate (" + + initialLightSensorRate + ") to be less than or equal to " + + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ")."); + } + + if (backlightValues != null && backlightValues.length > 0) { + final int bottom = backlightValues[0]; if (mScreenBrightnessDarkConfig > bottom) { Slog.w(TAG, "config_screenBrightnessDark (" + mScreenBrightnessDarkConfig + ") should be less than or equal to the first value of " @@ -411,13 +424,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (bottom < screenBrightnessRangeMinimum) { screenBrightnessRangeMinimum = bottom; } + } + + float[] nitsRange = { screenMinimumNits, screenMaximumNits }; + int[] backlightRange = { screenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum }; + + BrightnessMappingStrategy mapper = BrightnessMappingStrategy.create( + luxLevels, backlightValues, brightnessValuesNits, + nitsRange, backlightRange); + if (mapper != null) { mAutomaticBrightnessController = new AutomaticBrightnessController(this, - handler.getLooper(), sensorManager, screenAutoBrightnessSpline, - lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum, - mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate, - initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, + handler.getLooper(), sensorManager, mapper, lightSensorWarmUpTimeConfig, + screenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum, + dozeScaleFactor, lightSensorRate, initialLightSensorRate, + brighteningLightDebounce, darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientLightHorizon, autoBrightnessAdjustmentMaxGamma, dynamicHysteresis); + } else { + mUseSoftwareAutoBrightnessConfig = false; } } @@ -444,6 +468,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } + private static float[] getLuxLevels(int[] lux) { + // The first control point is implicit and always at 0 lux. + float[] levels = new float[lux.length + 1]; + for (int i = 0; i < lux.length; i++) { + levels[i + 1] = (float) lux[i]; + } + return levels; + } + + private static float[] getFloatArray(TypedArray array) { + final int N = array.length(); + float[] vals = new float[N]; + for (int i = 0; i < N; i++) { + vals[i] = array.getFloat(i, -1.0f); + } + array.recycle(); + return vals; + } + /** * Returns true if the proximity sensor screen-off function is available. */ @@ -512,7 +555,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (!mPendingUpdatePowerStateLocked) { mPendingUpdatePowerStateLocked = true; Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE); - msg.setAsynchronous(true); mHandler.sendMessage(msg); } } @@ -691,8 +733,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean userInitiatedChange = autoBrightnessAdjustmentChanged && mPowerRequest.brightnessSetByUser; mAutomaticBrightnessController.configure(autoBrightnessEnabled, - mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON, - userInitiatedChange); + mBrightnessConfiguration, mPowerRequest.screenAutoBrightnessAdjustment, + state != Display.STATE_ON, userInitiatedChange); } // Apply brightness boost. @@ -874,6 +916,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call sendUpdatePowerState(); } + public void setBrightnessConfiguration(BrightnessConfiguration c) { + Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c); + msg.sendToTarget(); + } + private void blockScreenOn() { if (mPendingScreenOnUnblocker == null) { Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0); @@ -1241,7 +1288,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Need to wait a little longer. // Debounce again later. We continue holding a wake lock while waiting. Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED); - msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime); } } @@ -1402,39 +1448,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) { - if (lux == null || lux.length == 0 || brightness == null || brightness.length == 0) { - Slog.e(TAG, "Could not create auto-brightness spline."); - return null; - } - try { - final int n = brightness.length; - float[] x = new float[n]; - float[] y = new float[n]; - y[0] = normalizeAbsoluteBrightness(brightness[0]); - for (int i = 1; i < n; i++) { - x[i] = lux[i - 1]; - y[i] = normalizeAbsoluteBrightness(brightness[i]); - } - - Spline spline = Spline.createSpline(x, y); - if (DEBUG) { - Slog.d(TAG, "Auto-brightness spline: " + spline); - for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) { - Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v))); - } - } - return spline; - } catch (IllegalArgumentException ex) { - Slog.e(TAG, "Could not create auto-brightness spline.", ex); - return null; - } - } - - private static float normalizeAbsoluteBrightness(int value) { - return (float)clampAbsoluteBrightness(value) / PowerManager.BRIGHTNESS_ON; - } - private static int clampAbsoluteBrightness(int value) { return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); } @@ -1467,6 +1480,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call updatePowerState(); } break; + case MSG_CONFIGURE_BRIGHTNESS: + BrightnessConfiguration c = (BrightnessConfiguration) msg.obj; + mBrightnessConfiguration = c != null ? c : mDefaultBrightnessConfiguration; + updatePowerState(); + break; } } } @@ -1492,17 +1510,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void onScreenOn() { Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this); - msg.setAsynchronous(true); mHandler.sendMessage(msg); } } private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener { - @Override public void onScreenOff() { Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this); - msg.setAsynchronous(true); mHandler.sendMessage(msg); } } diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index 34c8e22a9f1e..49b4465efac7 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -24,12 +24,17 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.graphics.Point; +import android.hardware.display.BrightnessConfiguration; import android.hardware.display.WifiDisplay; import android.util.AtomicFile; import android.util.Slog; +import android.util.SparseArray; +import android.util.Pair; import android.util.Xml; import android.view.Display; +import com.android.internal.annotations.VisibleForTesting; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; @@ -37,10 +42,12 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import libcore.io.IoUtils; @@ -57,14 +64,22 @@ import libcore.util.Objects; * <wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" /> * <remembered-wifi-displays> * <display-states> - * <display> + * <display unique-id="XXXXXXX"> * <color-mode>0</color-mode> * </display> * </display-states> * <stable-device-values> - * <stable-display-height>1920<stable-display-height> - * <stable-display-width>1080<stable-display-width> + * <stable-display-height>1920</stable-display-height> + * <stable-display-width>1080</stable-display-width> * </stable-device-values> + * <brightness-configurations> + * <brightness-configuration user-id="0"> + * <brightness-curve> + * <brightness-point lux="0" nits="13.25"/> + * <brightness-point lux="20" nits="35.94"/> + * </brightness-curve> + * </brightness-configuration> + * </brightness-configurations> * </display-manager-state> * </code> * @@ -73,6 +88,31 @@ import libcore.util.Objects; final class PersistentDataStore { static final String TAG = "DisplayManager"; + private static final String TAG_DISPLAY_MANAGER_STATE = "display-manager-state"; + + private static final String TAG_REMEMBERED_WIFI_DISPLAYS = "remembered-wifi-displays"; + private static final String TAG_WIFI_DISPLAY = "wifi-display"; + private static final String ATTR_DEVICE_ADDRESS = "deviceAddress"; + private static final String ATTR_DEVICE_NAME = "deviceName"; + private static final String ATTR_DEVICE_ALIAS = "deviceAlias"; + + private static final String TAG_DISPLAY_STATES = "display-states"; + private static final String TAG_DISPLAY = "display"; + private static final String TAG_COLOR_MODE = "color-mode"; + private static final String ATTR_UNIQUE_ID = "unique-id"; + + private static final String TAG_STABLE_DEVICE_VALUES = "stable-device-values"; + private static final String TAG_STABLE_DISPLAY_HEIGHT = "stable-display-height"; + private static final String TAG_STABLE_DISPLAY_WIDTH = "stable-display-width"; + + private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations"; + private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration"; + private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve"; + private static final String TAG_BRIGHTNESS_POINT = "brightness-point"; + private static final String ATTR_USER_SERIAL = "user-serial"; + private static final String ATTR_LUX = "lux"; + private static final String ATTR_NITS = "nits"; + // Remembered Wifi display devices. private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>(); @@ -83,8 +123,8 @@ final class PersistentDataStore { // Display values which should be stable across the device's lifetime. private final StableDeviceValues mStableDeviceValues = new StableDeviceValues(); - // The atomic file used to safely read or write the file. - private final AtomicFile mAtomicFile; + // Brightness configuration by user + private BrightnessConfigurations mBrightnessConfigurations = new BrightnessConfigurations(); // True if the data has been loaded. private boolean mLoaded; @@ -92,8 +132,16 @@ final class PersistentDataStore { // True if there are changes to be saved. private boolean mDirty; + // The interface for methods which should be replaced by the test harness. + private Injector mInjector; + public PersistentDataStore() { - mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml")); + this(new Injector()); + } + + @VisibleForTesting + PersistentDataStore(Injector injector) { + mInjector = injector; } public void saveIfNeeded() { @@ -225,6 +273,18 @@ final class PersistentDataStore { } } + public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial) { + loadIfNeeded(); + if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial)) { + setDirty(); + } + } + + public BrightnessConfiguration getBrightnessConfiguration(int userSerial) { + loadIfNeeded(); + return mBrightnessConfigurations.getBrightnessConfiguration(userSerial); + } + private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) { loadIfNeeded(); DisplayState state = mDisplayStates.get(uniqueId); @@ -256,7 +316,7 @@ final class PersistentDataStore { final InputStream is; try { - is = mAtomicFile.openRead(); + is = mInjector.openRead(); } catch (FileNotFoundException ex) { return; } @@ -278,9 +338,9 @@ final class PersistentDataStore { } private void save() { - final FileOutputStream os; + final OutputStream os; try { - os = mAtomicFile.startWrite(); + os = mInjector.startWrite(); boolean success = false; try { XmlSerializer serializer = new FastXmlSerializer(); @@ -289,11 +349,7 @@ final class PersistentDataStore { serializer.flush(); success = true; } finally { - if (success) { - mAtomicFile.finishWrite(os); - } else { - mAtomicFile.failWrite(os); - } + mInjector.finishWrite(os, success); } } catch (IOException ex) { Slog.w(TAG, "Failed to save display manager persistent store data.", ex); @@ -302,18 +358,21 @@ final class PersistentDataStore { private void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { - XmlUtils.beginDocument(parser, "display-manager-state"); + XmlUtils.beginDocument(parser, TAG_DISPLAY_MANAGER_STATE); final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { - if (parser.getName().equals("remembered-wifi-displays")) { + if (parser.getName().equals(TAG_REMEMBERED_WIFI_DISPLAYS)) { loadRememberedWifiDisplaysFromXml(parser); } - if (parser.getName().equals("display-states")) { + if (parser.getName().equals(TAG_DISPLAY_STATES)) { loadDisplaysFromXml(parser); } - if (parser.getName().equals("stable-device-values")) { + if (parser.getName().equals(TAG_STABLE_DEVICE_VALUES)) { mStableDeviceValues.loadFromXml(parser); } + if (parser.getName().equals(TAG_BRIGHTNESS_CONFIGURATIONS)) { + mBrightnessConfigurations.loadFromXml(parser); + } } } @@ -321,10 +380,10 @@ final class PersistentDataStore { throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { - if (parser.getName().equals("wifi-display")) { - String deviceAddress = parser.getAttributeValue(null, "deviceAddress"); - String deviceName = parser.getAttributeValue(null, "deviceName"); - String deviceAlias = parser.getAttributeValue(null, "deviceAlias"); + if (parser.getName().equals(TAG_WIFI_DISPLAY)) { + String deviceAddress = parser.getAttributeValue(null, ATTR_DEVICE_ADDRESS); + String deviceName = parser.getAttributeValue(null, ATTR_DEVICE_NAME); + String deviceAlias = parser.getAttributeValue(null, ATTR_DEVICE_ALIAS); if (deviceAddress == null || deviceName == null) { throw new XmlPullParserException( "Missing deviceAddress or deviceName attribute on wifi-display."); @@ -345,8 +404,8 @@ final class PersistentDataStore { throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { - if (parser.getName().equals("display")) { - String uniqueId = parser.getAttributeValue(null, "unique-id"); + if (parser.getName().equals(TAG_DISPLAY)) { + String uniqueId = parser.getAttributeValue(null, ATTR_UNIQUE_ID); if (uniqueId == null) { throw new XmlPullParserException( "Missing unique-id attribute on display."); @@ -365,32 +424,35 @@ final class PersistentDataStore { private void saveToXml(XmlSerializer serializer) throws IOException { serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - serializer.startTag(null, "display-manager-state"); - serializer.startTag(null, "remembered-wifi-displays"); + serializer.startTag(null, TAG_DISPLAY_MANAGER_STATE); + serializer.startTag(null, TAG_REMEMBERED_WIFI_DISPLAYS); for (WifiDisplay display : mRememberedWifiDisplays) { - serializer.startTag(null, "wifi-display"); - serializer.attribute(null, "deviceAddress", display.getDeviceAddress()); - serializer.attribute(null, "deviceName", display.getDeviceName()); + serializer.startTag(null, TAG_WIFI_DISPLAY); + serializer.attribute(null, ATTR_DEVICE_ADDRESS, display.getDeviceAddress()); + serializer.attribute(null, ATTR_DEVICE_NAME, display.getDeviceName()); if (display.getDeviceAlias() != null) { - serializer.attribute(null, "deviceAlias", display.getDeviceAlias()); + serializer.attribute(null, ATTR_DEVICE_ALIAS, display.getDeviceAlias()); } - serializer.endTag(null, "wifi-display"); + serializer.endTag(null, TAG_WIFI_DISPLAY); } - serializer.endTag(null, "remembered-wifi-displays"); - serializer.startTag(null, "display-states"); + serializer.endTag(null, TAG_REMEMBERED_WIFI_DISPLAYS); + serializer.startTag(null, TAG_DISPLAY_STATES); for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) { final String uniqueId = entry.getKey(); final DisplayState state = entry.getValue(); - serializer.startTag(null, "display"); - serializer.attribute(null, "unique-id", uniqueId); + serializer.startTag(null, TAG_DISPLAY); + serializer.attribute(null, ATTR_UNIQUE_ID, uniqueId); state.saveToXml(serializer); - serializer.endTag(null, "display"); + serializer.endTag(null, TAG_DISPLAY); } - serializer.endTag(null, "display-states"); - serializer.startTag(null, "stable-device-values"); + serializer.endTag(null, TAG_DISPLAY_STATES); + serializer.startTag(null, TAG_STABLE_DEVICE_VALUES); mStableDeviceValues.saveToXml(serializer); - serializer.endTag(null, "stable-device-values"); - serializer.endTag(null, "display-manager-state"); + serializer.endTag(null, TAG_STABLE_DEVICE_VALUES); + serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS); + mBrightnessConfigurations.saveToXml(serializer); + serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS); + serializer.endTag(null, TAG_DISPLAY_MANAGER_STATE); serializer.endDocument(); } @@ -411,6 +473,8 @@ final class PersistentDataStore { } pw.println(" StableDeviceValues:"); mStableDeviceValues.dump(pw, " "); + pw.println(" BrightnessConfigurations:"); + mBrightnessConfigurations.dump(pw, " "); } private static final class DisplayState { @@ -433,7 +497,7 @@ final class PersistentDataStore { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { - if (parser.getName().equals("color-mode")) { + if (parser.getName().equals(TAG_COLOR_MODE)) { String value = parser.nextText(); mColorMode = Integer.parseInt(value); } @@ -441,9 +505,9 @@ final class PersistentDataStore { } public void saveToXml(XmlSerializer serializer) throws IOException { - serializer.startTag(null, "color-mode"); + serializer.startTag(null, TAG_COLOR_MODE); serializer.text(Integer.toString(mColorMode)); - serializer.endTag(null, "color-mode"); + serializer.endTag(null, TAG_COLOR_MODE); } public void dump(final PrintWriter pw, final String prefix) { @@ -472,10 +536,10 @@ final class PersistentDataStore { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { switch (parser.getName()) { - case "stable-display-width": + case TAG_STABLE_DISPLAY_WIDTH: mWidth = loadIntValue(parser); break; - case "stable-display-height": + case TAG_STABLE_DISPLAY_HEIGHT: mHeight = loadIntValue(parser); break; } @@ -494,12 +558,12 @@ final class PersistentDataStore { public void saveToXml(XmlSerializer serializer) throws IOException { if (mWidth > 0 && mHeight > 0) { - serializer.startTag(null, "stable-display-width"); + serializer.startTag(null, TAG_STABLE_DISPLAY_WIDTH); serializer.text(Integer.toString(mWidth)); - serializer.endTag(null, "stable-display-width"); - serializer.startTag(null, "stable-display-height"); + serializer.endTag(null, TAG_STABLE_DISPLAY_WIDTH); + serializer.startTag(null, TAG_STABLE_DISPLAY_HEIGHT); serializer.text(Integer.toString(mHeight)); - serializer.endTag(null, "stable-display-height"); + serializer.endTag(null, TAG_STABLE_DISPLAY_HEIGHT); } } @@ -508,4 +572,158 @@ final class PersistentDataStore { pw.println(prefix + "StableDisplayHeight=" + mHeight); } } + + private static final class BrightnessConfigurations { + // Maps from a user ID to the users' given brightness configuration + private SparseArray<BrightnessConfiguration> mConfigurations; + + public BrightnessConfigurations() { + mConfigurations = new SparseArray<>(); + } + + private boolean setBrightnessConfigurationForUser(BrightnessConfiguration c, + int userSerial) { + BrightnessConfiguration currentConfig = mConfigurations.get(userSerial); + if (currentConfig == null || !currentConfig.equals(c)) { + mConfigurations.put(userSerial, c); + return true; + } + return false; + } + + public BrightnessConfiguration getBrightnessConfiguration(int userSerial) { + return mConfigurations.get(userSerial); + } + + public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (TAG_BRIGHTNESS_CONFIGURATION.equals(parser.getName())) { + int userSerial; + try { + userSerial = Integer.parseInt( + parser.getAttributeValue(null, ATTR_USER_SERIAL)); + } catch (NumberFormatException nfe) { + userSerial= -1; + Slog.e(TAG, "Failed to read in brightness configuration", nfe); + } + + try { + BrightnessConfiguration config = loadConfigurationFromXml(parser); + if (userSerial>= 0 && config != null) { + mConfigurations.put(userSerial, config); + } + } catch (IllegalArgumentException iae) { + Slog.e(TAG, "Failed to load brightness configuration!", iae); + } + } + } + } + + private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser) + throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) { + Pair<float[], float[]> curve = loadCurveFromXml(parser, builder); + builder.setCurve(curve.first /*lux*/, curve.second /*nits*/); + } + } + return builder.build(); + } + + private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser, + BrightnessConfiguration.Builder builder) + throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + List<Float> luxLevels = new ArrayList<>(); + List<Float> nitLevels = new ArrayList<>(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (TAG_BRIGHTNESS_POINT.equals(parser.getName())) { + luxLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_LUX))); + nitLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_NITS))); + } + } + final int N = luxLevels.size(); + float[] lux = new float[N]; + float[] nits = new float[N]; + for (int i = 0; i < N; i++) { + lux[i] = luxLevels.get(i); + nits[i] = nitLevels.get(i); + } + return Pair.create(lux, nits); + } + + private static float loadFloat(String val) { + try { + return Float.parseFloat(val); + } catch (NullPointerException | NumberFormatException e) { + Slog.e(TAG, "Failed to parse float loading brightness config", e); + return Float.NEGATIVE_INFINITY; + } + } + + public void saveToXml(XmlSerializer serializer) throws IOException { + for (int i = 0; i < mConfigurations.size(); i++) { + final int userSerial= mConfigurations.keyAt(i); + final BrightnessConfiguration config = mConfigurations.valueAt(i); + + serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION); + serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial)); + saveConfigurationToXml(serializer, config); + serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION); + } + } + + private static void saveConfigurationToXml(XmlSerializer serializer, + BrightnessConfiguration config) throws IOException { + serializer.startTag(null, TAG_BRIGHTNESS_CURVE); + final Pair<float[], float[]> curve = config.getCurve(); + for (int i = 0; i < curve.first.length; i++) { + serializer.startTag(null, TAG_BRIGHTNESS_POINT); + serializer.attribute(null, ATTR_LUX, Float.toString(curve.first[i])); + serializer.attribute(null, ATTR_NITS, Float.toString(curve.second[i])); + serializer.endTag(null, TAG_BRIGHTNESS_POINT); + } + serializer.endTag(null, TAG_BRIGHTNESS_CURVE); + } + + public void dump(final PrintWriter pw, final String prefix) { + for (int i = 0; i < mConfigurations.size(); i++) { + final int userSerial= mConfigurations.keyAt(i); + pw.println(prefix + "User " + userSerial + ":"); + pw.println(prefix + " " + mConfigurations.valueAt(i)); + } + } + } + + @VisibleForTesting + static class Injector { + private final AtomicFile mAtomicFile; + + public Injector() { + mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml")); + } + + public InputStream openRead() throws FileNotFoundException { + return mAtomicFile.openRead(); + } + + public OutputStream startWrite() throws IOException { + return mAtomicFile.startWrite(); + } + + public void finishWrite(OutputStream os, boolean success) { + if (!(os instanceof FileOutputStream)) { + throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os); + } + FileOutputStream fos = (FileOutputStream) os; + if (success) { + mAtomicFile.finishWrite(fos); + } else { + mAtomicFile.failWrite(fos); + } + } + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 4c994b94e71b..7aa628af4621 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1088,14 +1088,17 @@ public final class SystemServer { } traceEnd(); - // Wifi Service must be started first for wifi-related services. - traceBeginAndSlog("StartWifi"); - mSystemServiceManager.startService(WIFI_SERVICE_CLASS); - traceEnd(); - traceBeginAndSlog("StartWifiScanning"); - mSystemServiceManager.startService( - "com.android.server.wifi.scanner.WifiScanningService"); - traceEnd(); + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI)) { + // Wifi Service must be started first for wifi-related services. + traceBeginAndSlog("StartWifi"); + mSystemServiceManager.startService(WIFI_SERVICE_CLASS); + traceEnd(); + traceBeginAndSlog("StartWifiScanning"); + mSystemServiceManager.startService( + "com.android.server.wifi.scanner.WifiScanningService"); + traceEnd(); + } if (!disableRtt) { traceBeginAndSlog("StartWifiRtt"); diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java new file mode 100644 index 000000000000..2629b12375a4 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java @@ -0,0 +1,230 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.display; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.os.PowerManager; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.Spline; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BrightnessMappingStrategyTest { + + private static final float[] LUX_LEVELS = { + 0f, + 5f, + 20f, + 40f, + 100f, + 325f, + 600f, + 1250f, + 2200f, + 4000f, + 5000f + }; + + private static final float[] DISPLAY_LEVELS_NITS = { + 13.25f, + 54.0f, + 78.85f, + 105.02f, + 132.7f, + 170.12f, + 212.1f, + 265.2f, + 335.8f, + 415.2f, + 478.5f, + }; + + private static final int[] DISPLAY_LEVELS_BACKLIGHT = { + 9, + 30, + 45, + 62, + 78, + 96, + 119, + 146, + 178, + 221, + 255 + }; + + private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f }; + private static final int[] BACKLIGHT_RANGE = { 1, 255 }; + + @Test + public void testSimpleStrategyMappingAtControlPoints() { + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create( + LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, + null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/); + assertNotNull("BrightnessMappingStrategy should not be null", simple); + for (int i = 0; i < LUX_LEVELS.length; i++) { + final float expectedLevel = + (float) DISPLAY_LEVELS_BACKLIGHT[i] / PowerManager.BRIGHTNESS_ON; + assertEquals(expectedLevel, + simple.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/); + } + } + + @Test + public void testSimpleStrategyMappingBetweenControlPoints() { + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create( + LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, + null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/); + assertNotNull("BrightnessMappingStrategy should not be null", simple); + for (int i = 1; i < LUX_LEVELS.length; i++) { + final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; + final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON; + assertTrue("Desired brightness should be between adjacent control points.", + backlight > DISPLAY_LEVELS_BACKLIGHT[i-1] + && backlight < DISPLAY_LEVELS_BACKLIGHT[i]); + } + } + + @Test + public void testPhysicalStrategyMappingAtControlPoints() { + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create( + LUX_LEVELS, null /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + assertNotNull("BrightnessMappingStrategy should not be null", physical); + for (int i = 0; i < LUX_LEVELS.length; i++) { + final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1]; + assertEquals(expectedLevel, + physical.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/); + } + } + + @Test + public void testPhysicalStrategyMappingBetweenControlPoints() { + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create( + LUX_LEVELS, null /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + assertNotNull("BrightnessMappingStrategy should not be null", physical); + Spline backlightToBrightness = + Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS); + for (int i = 1; i < LUX_LEVELS.length; i++) { + final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; + final float backlight = physical.getBrightness(lux) * PowerManager.BRIGHTNESS_ON; + final float nits = backlightToBrightness.interpolate(backlight); + assertTrue("Desired brightness should be between adjacent control points.", + nits > DISPLAY_LEVELS_NITS[i-1] && nits < DISPLAY_LEVELS_NITS[i]); + } + } + + @Test + public void testDefaultStrategyIsPhysical() { + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create( + LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, + DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy); + } + + @Test + public void testNonStrictlyIncreasingLuxLevelsFails() { + final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length); + final int idx = lux.length / 2; + float tmp = lux[idx]; + lux[idx] = lux[idx+1]; + lux[idx+1] = tmp; + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create( + lux, null /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + assertNull(strategy); + + // And make sure we get the same result even if it's monotone but not increasing. + lux[idx] = lux[idx+1]; + strategy = BrightnessMappingStrategy.create( + lux, null /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + assertNull(strategy); + } + + @Test + public void testDifferentNumberOfControlPointValuesFails() { + //Extra lux level + final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1); + // Make sure it's strictly increasing so that the only failure is the differing array + // lengths + lux[lux.length - 1] = lux[lux.length - 2] + 1; + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create( + lux, null /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + assertNull(strategy); + + strategy = BrightnessMappingStrategy.create( + lux, DISPLAY_LEVELS_BACKLIGHT, + null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/); + assertNull(strategy); + + // Extra backlight level + final int[] backlight = Arrays.copyOf( + DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1); + backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1; + strategy = BrightnessMappingStrategy.create( + LUX_LEVELS, backlight, + null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/); + assertNull(strategy); + + // Extra nits level + final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1); + nits[nits.length - 1] = nits[nits.length - 2] + 1; + strategy = BrightnessMappingStrategy.create( + LUX_LEVELS, null /*brightnessLevelsBacklight*/, + nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); + assertNull(strategy); + } + + @Test + public void testPhysicalStrategyRequiresNitsMapping() { + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create( + LUX_LEVELS, null /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, null, BACKLIGHT_RANGE); + assertNull(physical); + + physical = BrightnessMappingStrategy.create( + LUX_LEVELS, null /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, null); + assertNull(physical); + + physical = BrightnessMappingStrategy.create( + LUX_LEVELS, null /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS, null, null); + assertNull(physical); + } + + private static float[] toFloatArray(int[] vals) { + float[] newVals = new float[vals.length]; + for (int i = 0; i < vals.length; i++) { + newVals[i] = (float) vals[i]; + } + return newVals; + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java new file mode 100644 index 000000000000..0cc37b48184e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java @@ -0,0 +1,223 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.display; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.hardware.display.BrightnessConfiguration; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.AtomicFile; +import android.util.Pair; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.FileInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class PersistentDataStoreTest { + private PersistentDataStore mDataStore; + private TestInjector mInjector; + + @Before + public void setUp() { + mInjector = new TestInjector(); + mDataStore = new PersistentDataStore(mInjector); + } + + @Test + public void testLoadingBrightnessConfigurations() { + String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<display-manager-state>\n" + + " <brightness-configurations>\n" + + " <brightness-configuration user-serial=\"1\">\n" + + " <brightness-curve>\n" + + " <brightness-point lux=\"0\" nits=\"13.25\"/>\n" + + " <brightness-point lux=\"25\" nits=\"35.94\"/>\n" + + " </brightness-curve>\n" + + " </brightness-configuration>\n" + + " <brightness-configuration user-serial=\"3\">\n" + + " <brightness-curve>\n" + + " <brightness-point lux=\"0\" nits=\"13.25\"/>\n" + + " <brightness-point lux=\"10.2\" nits=\"15\"/>\n" + + " </brightness-curve>\n" + + " </brightness-configuration>\n" + + " </brightness-configurations>\n" + + "</display-manager-state>\n"; + InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); + mInjector.setReadStream(is); + mDataStore.loadIfNeeded(); + BrightnessConfiguration config = mDataStore.getBrightnessConfiguration(1 /*userSerial*/); + Pair<float[], float[]> curve = config.getCurve(); + float[] expectedLux = { 0f, 25f }; + float[] expectedNits = { 13.25f, 35.94f }; + assertArrayEquals(expectedLux, curve.first, "lux"); + assertArrayEquals(expectedNits, curve.second, "nits"); + + config = mDataStore.getBrightnessConfiguration(3 /*userSerial*/); + curve = config.getCurve(); + expectedLux = new float[] { 0f, 10.2f }; + expectedNits = new float[] { 13.25f, 15f }; + assertArrayEquals(expectedLux, curve.first, "lux"); + assertArrayEquals(expectedNits, curve.second, "nits"); + } + + @Test + public void testBrightnessConfigWithInvalidCurveIsIgnored() { + String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<display-manager-state>\n" + + " <brightness-configurations>\n" + + " <brightness-configuration user-serial=\"0\">\n" + + " <brightness-curve>\n" + + " <brightness-point lux=\"1\" nits=\"13.25\"/>\n" + + " <brightness-point lux=\"25\" nits=\"35.94\"/>\n" + + " </brightness-curve>\n" + + " </brightness-configuration>\n" + + " </brightness-configurations>\n" + + "</display-manager-state>\n"; + InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); + mInjector.setReadStream(is); + mDataStore.loadIfNeeded(); + assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); + } + + @Test + public void testBrightnessConfigWithInvalidFloatsIsIgnored() { + String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<display-manager-state>\n" + + " <brightness-configurations>\n" + + " <brightness-configuration user-serial=\"0\">\n" + + " <brightness-curve>\n" + + " <brightness-point lux=\"0\" nits=\"13.25\"/>\n" + + " <brightness-point lux=\"0xFF\" nits=\"foo\"/>\n" + + " </brightness-curve>\n" + + " </brightness-configuration>\n" + + " </brightness-configurations>\n" + + "</display-manager-state>\n"; + InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); + mInjector.setReadStream(is); + mDataStore.loadIfNeeded(); + assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); + } + + @Test + public void testEmptyBrightnessConfigurationsDoesntCrash() { + String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<display-manager-state>\n" + + " <brightness-configurations />\n" + + "</display-manager-state>\n"; + InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); + mInjector.setReadStream(is); + mDataStore.loadIfNeeded(); + assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); + } + + @Test + public void testStoreAndReloadOfBrightnessConfigurations() { + final float[] lux = { 0f, 10f }; + final float[] nits = {1f, 100f }; + final BrightnessConfiguration config = new BrightnessConfiguration.Builder() + .setCurve(lux, nits) + .build(); + mDataStore.loadIfNeeded(); + assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/)); + mDataStore.setBrightnessConfigurationForUser(config, 0); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + mInjector.setWriteStream(baos); + mDataStore.saveIfNeeded(); + assertTrue(mInjector.wasWriteSuccessful()); + + TestInjector newInjector = new TestInjector(); + PersistentDataStore newDataStore = new PersistentDataStore(newInjector); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + newInjector.setReadStream(bais); + newDataStore.loadIfNeeded(); + assertNotNull(newDataStore.getBrightnessConfiguration(0 /*userSerial*/)); + assertEquals(mDataStore.getBrightnessConfiguration(0 /*userSerial*/), + newDataStore.getBrightnessConfiguration(0 /*userSerial*/)); + } + + public class TestInjector extends PersistentDataStore.Injector { + private InputStream mReadStream; + private OutputStream mWriteStream; + + private boolean mWasSuccessful; + + @Override + public InputStream openRead() throws FileNotFoundException { + if (mReadStream != null) { + return mReadStream; + } else { + throw new FileNotFoundException(); + } + } + + @Override + public OutputStream startWrite() { + return mWriteStream; + } + + @Override + public void finishWrite(OutputStream os, boolean success) { + mWasSuccessful = success; + try { + os.close(); + } catch (IOException e) { + // This method can't throw IOException since the super implementation doesn't, so + // we just wrap it in a RuntimeException so we end up crashing the test all the + // same. + throw new RuntimeException(e); + } + } + + public void setReadStream(InputStream is) { + mReadStream = is; + } + + public void setWriteStream(OutputStream os) { + mWriteStream = os; + } + + public boolean wasWriteSuccessful() { + return mWasSuccessful; + } + } + + private static void assertArrayEquals(float[] expected, float[] actual, String name) { + assertEquals("Expected " + name + " arrays to be the same length!", + expected.length, actual.length); + for (int i = 0; i < expected.length; i++) { + assertEquals("Expected " + name + " arrays to be equivalent when value " + i + + "differs", expected[i], actual[i], 0.01 /*tolerance*/); + } + } +} diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java index 21b11b058160..82f800128beb 100644 --- a/services/usage/java/com/android/server/usage/StorageStatsService.java +++ b/services/usage/java/com/android/server/usage/StorageStatsService.java @@ -164,6 +164,14 @@ public class StorageStatsService extends IStorageStatsManager.Stub { } @Override + public boolean isReservedSupported(String volumeUuid, String callingPackage) { + enforcePermission(Binder.getCallingUid(), callingPackage); + + // TODO: implement as part of b/62024591 + return false; + } + + @Override public long getTotalBytes(String volumeUuid, String callingPackage) { // NOTE: No permissions required diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 70016157960a..dec7b762459d 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -21,6 +21,7 @@ import android.app.Service; import android.content.ComponentName; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -145,6 +146,7 @@ public abstract class ConnectionService extends Service { private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR"; private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL"; private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG"; + private static final String SESSION_HANDOVER_FAILED = "CS.haF"; private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1; private static final int MSG_CREATE_CONNECTION = 2; @@ -176,6 +178,7 @@ public abstract class ConnectionService extends Service { private static final int MSG_CREATE_CONNECTION_COMPLETE = 29; private static final int MSG_CONNECTION_SERVICE_FOCUS_LOST = 30; private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31; + private static final int MSG_HANDOVER_FAILED = 32; private static Connection sNullConnection; @@ -279,6 +282,22 @@ public abstract class ConnectionService extends Service { } @Override + public void handoverFailed(String callId, ConnectionRequest request, int reason, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = request; + args.arg3 = Log.createSubsession(); + args.arg4 = reason; + mHandler.obtainMessage(MSG_HANDOVER_FAILED, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void abort(String callId, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_ABORT); try { @@ -747,6 +766,36 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_HANDOVER_FAILED: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg3, SESSION_HANDLER + + SESSION_HANDOVER_FAILED); + try { + final String id = (String) args.arg1; + final ConnectionRequest request = (ConnectionRequest) args.arg2; + final int reason = (int) args.arg4; + if (!mAreAccountsInitialized) { + Log.d(this, "Enqueueing pre-init request %s", id); + mPreInitializationConnectionRequests.add( + new android.telecom.Logging.Runnable( + SESSION_HANDLER + + SESSION_HANDOVER_FAILED + ".pICR", + null /*lock*/) { + @Override + public void loggedRun() { + handoverFailed(id, request, reason); + } + }.prepare()); + } else { + Log.i(this, "createConnectionFailed %s", id); + handoverFailed(id, request, reason); + } + } finally { + args.recycle(); + Log.endSession(); + } + break; + } case MSG_ABORT: { SomeArgs args = (SomeArgs) msg.obj; Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ABORT); @@ -1402,12 +1451,13 @@ public abstract class ConnectionService extends Service { isUnknown); Connection connection = null; - if (request.getExtras() != null && request.getExtras().getBoolean( - TelecomManager.EXTRA_IS_HANDOVER,false)) { + if (getApplicationContext().getApplicationInfo().targetSdkVersion > + Build.VERSION_CODES.O_MR1 && request.getExtras() != null && + request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER,false)) { if (!isIncoming) { connection = onCreateOutgoingHandoverConnection(callManagerAccount, request); } else { - // Todo: Call onCreateIncommingHandoverConnection() + connection = onCreateIncomingHandoverConnection(callManagerAccount, request); } } else { connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request) @@ -1482,6 +1532,13 @@ public abstract class ConnectionService extends Service { } } + private void handoverFailed(final String callId, final ConnectionRequest request, + int reason) { + + Log.i(this, "handoverFailed %s", callId); + onHandoverFailed(request, reason); + } + /** * Called by Telecom when the creation of a new Connection has completed and it is now added * to Telecom. diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index a74056607a24..3d04bfc1bae5 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -104,4 +104,7 @@ oneway interface IConnectionService { void connectionServiceFocusLost(in Session.Info sessionInfo); void connectionServiceFocusGained(in Session.Info sessionInfo); + + void handoverFailed(String callId, in ConnectionRequest request, + int error, in Session.Info sessionInfo); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index c050a1029eb0..ead8849c363e 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -735,12 +735,11 @@ public class CarrierConfigManager { public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool"; /** - * Flag specifying whether signal strength is hidden in SIM Status screen, - * default to false. - * @hide + * Flag specifying whether the {@link android.telephony.SignalStrength} is shown in the SIM + * Status screen. The default value is true. */ - public static final String KEY_HIDE_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = - "hide_signal_strength_in_sim_status_bool"; + public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = + "show_signal_strength_in_sim_status_bool"; /** * Flag specifying whether an additional (client initiated) intent needs to be sent on System @@ -1801,7 +1800,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING, ""); sDefaults.putStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY, null); sDefaults.putBoolean(KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL, false); - sDefaults.putBoolean(KEY_HIDE_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, false); + sDefaults.putBoolean(KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, true); sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false); sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING, ""); sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, ""); |