diff --git a/device.mk b/device.mk
index c1f6a56..e3c47d7 100644
--- a/device.mk
+++ b/device.mk
@@ -115,6 +115,10 @@ PRODUCT_PACKAGES += \
android.hardware.ir@1.0-impl \
android.hardware.ir@1.0-service
+# Device-specific settings
+PRODUCT_PACKAGES += \
+ XiaomiParts
+
# Dex
PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD := false
PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER := verify
diff --git a/parts/Android.bp b/parts/Android.bp
new file mode 100644
index 0000000..82c6e13
--- /dev/null
+++ b/parts/Android.bp
@@ -0,0 +1,25 @@
+//
+// Copyright (C) 2017-2020 The LineageOS Project
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+
+android_app {
+ name: "XiaomiParts",
+
+ srcs: ["src/**/*.java"],
+
+ certificate: "platform",
+ platform_apis: true,
+ system_ext_specific: true,
+ privileged: true,
+
+ static_libs: [
+ "org.lineageos.settings.resources",
+ ],
+
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
+
+}
diff --git a/parts/AndroidManifest.xml b/parts/AndroidManifest.xml
new file mode 100644
index 0000000..cdd3f84
--- /dev/null
+++ b/parts/AndroidManifest.xml
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/parts/proguard.flags b/parts/proguard.flags
new file mode 100644
index 0000000..e69de29
diff --git a/parts/res/drawable/ic_clear_speaker.xml b/parts/res/drawable/ic_clear_speaker.xml
new file mode 100644
index 0000000..4e2981d
--- /dev/null
+++ b/parts/res/drawable/ic_clear_speaker.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/parts/res/drawable/ic_refresh_120.xml b/parts/res/drawable/ic_refresh_120.xml
new file mode 100644
index 0000000..f81418b
--- /dev/null
+++ b/parts/res/drawable/ic_refresh_120.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/parts/res/drawable/ic_refresh_60.xml b/parts/res/drawable/ic_refresh_60.xml
new file mode 100644
index 0000000..2c4a62f
--- /dev/null
+++ b/parts/res/drawable/ic_refresh_60.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/parts/res/drawable/ic_refresh_90.xml b/parts/res/drawable/ic_refresh_90.xml
new file mode 100644
index 0000000..71ea123
--- /dev/null
+++ b/parts/res/drawable/ic_refresh_90.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/parts/res/drawable/ic_refresh_default.xml b/parts/res/drawable/ic_refresh_default.xml
new file mode 100644
index 0000000..a4b7d3a
--- /dev/null
+++ b/parts/res/drawable/ic_refresh_default.xml
@@ -0,0 +1,11 @@
+
+
+
+
\ No newline at end of file
diff --git a/parts/res/drawable/ic_thermal_benchmark.xml b/parts/res/drawable/ic_thermal_benchmark.xml
new file mode 100644
index 0000000..8b3f2c1
--- /dev/null
+++ b/parts/res/drawable/ic_thermal_benchmark.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/parts/res/drawable/ic_thermal_browser.xml b/parts/res/drawable/ic_thermal_browser.xml
new file mode 100644
index 0000000..73880a0
--- /dev/null
+++ b/parts/res/drawable/ic_thermal_browser.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/parts/res/drawable/ic_thermal_camera.xml b/parts/res/drawable/ic_thermal_camera.xml
new file mode 100644
index 0000000..642c082
--- /dev/null
+++ b/parts/res/drawable/ic_thermal_camera.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/parts/res/drawable/ic_thermal_default.xml b/parts/res/drawable/ic_thermal_default.xml
new file mode 100644
index 0000000..cc78c36
--- /dev/null
+++ b/parts/res/drawable/ic_thermal_default.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/parts/res/drawable/ic_thermal_dialer.xml b/parts/res/drawable/ic_thermal_dialer.xml
new file mode 100644
index 0000000..f87e39b
--- /dev/null
+++ b/parts/res/drawable/ic_thermal_dialer.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/parts/res/drawable/ic_thermal_gaming.xml b/parts/res/drawable/ic_thermal_gaming.xml
new file mode 100644
index 0000000..29a13b8
--- /dev/null
+++ b/parts/res/drawable/ic_thermal_gaming.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/parts/res/drawable/ic_thermal_streaming.xml b/parts/res/drawable/ic_thermal_streaming.xml
new file mode 100644
index 0000000..e6e272b
--- /dev/null
+++ b/parts/res/drawable/ic_thermal_streaming.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/parts/res/layout/refresh_layout.xml b/parts/res/layout/refresh_layout.xml
new file mode 100644
index 0000000..6467efa
--- /dev/null
+++ b/parts/res/layout/refresh_layout.xml
@@ -0,0 +1,18 @@
+
+
+
\ No newline at end of file
diff --git a/parts/res/layout/refresh_list_item.xml b/parts/res/layout/refresh_list_item.xml
new file mode 100644
index 0000000..e2ce15f
--- /dev/null
+++ b/parts/res/layout/refresh_list_item.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/parts/res/layout/thermal_layout.xml b/parts/res/layout/thermal_layout.xml
new file mode 100644
index 0000000..3982a35
--- /dev/null
+++ b/parts/res/layout/thermal_layout.xml
@@ -0,0 +1,18 @@
+
+
+
diff --git a/parts/res/layout/thermal_list_item.xml b/parts/res/layout/thermal_list_item.xml
new file mode 100644
index 0000000..864b7d9
--- /dev/null
+++ b/parts/res/layout/thermal_list_item.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/parts/res/raw/clear_speaker_sound.mp3 b/parts/res/raw/clear_speaker_sound.mp3
new file mode 100644
index 0000000..a67b135
Binary files /dev/null and b/parts/res/raw/clear_speaker_sound.mp3 differ
diff --git a/parts/res/values/strings.xml b/parts/res/values/strings.xml
new file mode 100644
index 0000000..7f33677
--- /dev/null
+++ b/parts/res/values/strings.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+ Touch Responsiveness
+ Increase Touch Responsiveness
+ Increases touch polling rate to decrease latency
+
+
+ Thermal Profiles
+ Adjust per-app thermal profiles for optimum performance
+ Default
+ Benchmark
+ Browser
+ Camera
+ Dialer
+ Gaming
+ Streaming
+
+
+ Refresh Rate
+
+
+ Clear speaker
+ Play a 30-second audio to clear the speaker
+ Run this feature once or twice if you find that your speaker is lightly blocked by dust. Set media volume to maximum.\n\nIf the speaker is blocked heavily, run this feature 2-5 times while shaking your device with the speaker facing downwards.\n\nWARNING: Ensure that any headphones are unplugged.
+
+
+ Per-app refresh rate
+ Set the maximum refresh rate for a specific application
+ Default
+ 60Hz
+ 90Hz
+ 120Hz
+
diff --git a/parts/res/values/themes.xml b/parts/res/values/themes.xml
new file mode 100644
index 0000000..a4438d3
--- /dev/null
+++ b/parts/res/values/themes.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
diff --git a/parts/res/xml/clear_speaker_settings.xml b/parts/res/xml/clear_speaker_settings.xml
new file mode 100644
index 0000000..8716efb
--- /dev/null
+++ b/parts/res/xml/clear_speaker_settings.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
diff --git a/parts/src/org/lineageos/settings/BootCompletedReceiver.java b/parts/src/org/lineageos/settings/BootCompletedReceiver.java
new file mode 100644
index 0000000..5ca435b
--- /dev/null
+++ b/parts/src/org/lineageos/settings/BootCompletedReceiver.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod Project
+ * 2017-2019 The LineageOS 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 org.lineageos.settings;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import org.lineageos.settings.thermal.ThermalUtils;
+import org.lineageos.settings.refreshrate.RefreshUtils;
+
+public class BootCompletedReceiver extends BroadcastReceiver {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "XiaomiParts";
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+
+ if (DEBUG)
+ Log.d(TAG, "Received boot completed intent");
+ ThermalUtils.startService(context);
+ RefreshUtils.initialize(context);
+ }
+}
diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshActivity.java b/parts/src/org/lineageos/settings/refreshrate/RefreshActivity.java
new file mode 100644
index 0000000..2ecf2a4
--- /dev/null
+++ b/parts/src/org/lineageos/settings/refreshrate/RefreshActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020-2022 The LineageOS 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 org.lineageos.settings.refreshrate;
+
+import android.os.Bundle;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+import com.android.settingslib.widget.R;
+
+public class RefreshActivity extends CollapsingToolbarBaseActivity {
+ private static final String TAG_REFRESH = "refresh";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getFragmentManager().beginTransaction().replace(R.id.content_frame,
+ new RefreshSettingsFragment(), TAG_REFRESH).commit();
+ }
+}
diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshService.java b/parts/src/org/lineageos/settings/refreshrate/RefreshService.java
new file mode 100644
index 0000000..fff9e69
--- /dev/null
+++ b/parts/src/org/lineageos/settings/refreshrate/RefreshService.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The LineageOS 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 org.lineageos.settings.refreshrate;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
+import android.app.IActivityTaskManager;
+import android.app.Service;
+import android.app.TaskStackListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.os.RemoteException;
+
+public class RefreshService extends Service {
+
+ private static final String TAG = "RefreshService";
+ private static final boolean DEBUG = false;
+
+ private String mPreviousApp;
+ private RefreshUtils mRefreshUtils;
+ private IActivityTaskManager mActivityTaskManager;
+ private final TaskStackListener mTaskListener = new TaskStackListener() {
+ @Override
+ public void onTaskStackChanged() {
+ try {
+ final RootTaskInfo info = mActivityTaskManager.getFocusedRootTaskInfo();
+ if (info == null || info.topActivity == null) {
+ return;
+ }
+ String foregroundApp = info.topActivity.getPackageName();
+ if (!foregroundApp.equals(mPreviousApp)) {
+ mRefreshUtils.setRefreshRate(foregroundApp);
+ mPreviousApp = foregroundApp;
+ }
+ } catch (Exception e) {}
+ }
+ };
+
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mPreviousApp = "";
+ mRefreshUtils.setDefaultRefreshRate(context);
+ }
+ };
+
+ @Override
+ public void onCreate() {
+ if (DEBUG) Log.d(TAG, "Creating service");
+ mRefreshUtils = new RefreshUtils(this);
+ mRefreshUtils.setDefaultRefreshRate(this);
+ try {
+ mActivityTaskManager = ActivityTaskManager.getService();
+ mActivityTaskManager.registerTaskStackListener(mTaskListener);
+ } catch (RemoteException e) {
+ // Do nothing
+ }
+ registerReceiver();
+ super.onCreate();
+ }
+
+ @Override
+ public void onDestroy() {
+ if (DEBUG) Log.d(TAG, "Destroying service");
+ unregisterReceiver();
+ try {
+ ActivityTaskManager.getService().unregisterTaskStackListener(mTaskListener);
+ } catch (RemoteException e) {
+ // Do nothing
+ }
+ mRefreshUtils.setDefaultRefreshRate(this);
+ super.onDestroy();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (DEBUG) Log.d(TAG, "Starting service");
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ private void registerReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ this.registerReceiver(mIntentReceiver, filter);
+ }
+
+ private void unregisterReceiver() {
+ this.unregisterReceiver(mIntentReceiver);
+ }
+}
diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshSettingsFragment.java b/parts/src/org/lineageos/settings/refreshrate/RefreshSettingsFragment.java
new file mode 100644
index 0000000..f43a8ac
--- /dev/null
+++ b/parts/src/org/lineageos/settings/refreshrate/RefreshSettingsFragment.java
@@ -0,0 +1,421 @@
+/**
+ * Copyright (C) 2020 The LineageOS 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 org.lineageos.settings.refreshrate;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.preference.PreferenceFragment;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.android.settingslib.applications.ApplicationsState;
+
+import org.lineageos.settings.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RefreshSettingsFragment extends PreferenceFragment
+ implements ApplicationsState.Callbacks {
+
+ private AllPackagesAdapter mAllPackagesAdapter;
+ private ApplicationsState mApplicationsState;
+ private ApplicationsState.Session mSession;
+ private ActivityFilter mActivityFilter;
+ private Map mEntryMap =
+ new HashMap();
+
+ private RefreshUtils mRefreshUtils;
+ private RecyclerView mAppsRecyclerView;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
+ mSession = mApplicationsState.newSession(this);
+ mSession.onResume();
+ mActivityFilter = new ActivityFilter(getActivity().getPackageManager());
+
+ mAllPackagesAdapter = new AllPackagesAdapter(getActivity());
+
+ mRefreshUtils = new RefreshUtils(getActivity());
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.refresh_layout, container, false);
+ }
+
+ @Override
+ public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ mAppsRecyclerView = view.findViewById(R.id.refresh_rv_view);
+ mAppsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+ mAppsRecyclerView.setAdapter(mAllPackagesAdapter);
+ }
+
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ rebuild();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ mSession.onPause();
+ mSession.onDestroy();
+ }
+
+ @Override
+ public void onPackageListChanged() {
+ mActivityFilter.updateLauncherInfoList();
+ rebuild();
+ }
+
+ @Override
+ public void onRebuildComplete(ArrayList entries) {
+ if (entries != null) {
+ handleAppEntries(entries);
+ mAllPackagesAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onLoadEntriesCompleted() {
+ rebuild();
+ }
+
+ @Override
+ public void onAllSizesComputed() {
+ }
+
+ @Override
+ public void onLauncherInfoChanged() {
+ }
+
+ @Override
+ public void onPackageIconChanged() {
+ }
+
+ @Override
+ public void onPackageSizeChanged(String packageName) {
+ }
+
+ @Override
+ public void onRunningStateChanged(boolean running) {
+ }
+
+ private void handleAppEntries(List entries) {
+ final ArrayList sections = new ArrayList();
+ final ArrayList positions = new ArrayList();
+ final PackageManager pm = getActivity().getPackageManager();
+ String lastSectionIndex = null;
+ int offset = 0;
+
+ for (int i = 0; i < entries.size(); i++) {
+ final ApplicationInfo info = entries.get(i).info;
+ final String label = (String) info.loadLabel(pm);
+ final String sectionIndex;
+
+ if (!info.enabled) {
+ sectionIndex = "--"; // XXX
+ } else if (TextUtils.isEmpty(label)) {
+ sectionIndex = "";
+ } else {
+ sectionIndex = label.substring(0, 1).toUpperCase();
+ }
+
+ if (lastSectionIndex == null ||
+ !TextUtils.equals(sectionIndex, lastSectionIndex)) {
+ sections.add(sectionIndex);
+ positions.add(offset);
+ lastSectionIndex = sectionIndex;
+ }
+
+ offset++;
+ }
+
+ mAllPackagesAdapter.setEntries(entries, sections, positions);
+ mEntryMap.clear();
+ for (ApplicationsState.AppEntry e : entries) {
+ mEntryMap.put(e.info.packageName, e);
+ }
+ }
+
+ private void rebuild() {
+ mSession.rebuild(mActivityFilter, ApplicationsState.ALPHA_COMPARATOR);
+ }
+
+ private int getStateDrawable(int state) {
+ switch (state) {
+ case RefreshUtils.STATE_STANDARD:
+ return R.drawable.ic_refresh_60;
+ case RefreshUtils.STATE_HIGH:
+ return R.drawable.ic_refresh_90;
+ case RefreshUtils.STATE_EXTREME:
+ return R.drawable.ic_refresh_120;
+ case RefreshUtils.STATE_DEFAULT:
+ default:
+ return R.drawable.ic_refresh_default;
+ }
+ }
+
+ private class ViewHolder extends RecyclerView.ViewHolder {
+ private TextView title;
+ private Spinner mode;
+ private ImageView icon;
+ private View rootView;
+ private ImageView stateIcon;
+
+ private ViewHolder(View view) {
+ super(view);
+ this.title = view.findViewById(R.id.app_name);
+ this.mode = view.findViewById(R.id.app_mode);
+ this.icon = view.findViewById(R.id.app_icon);
+ this.stateIcon = view.findViewById(R.id.state);
+ this.rootView = view;
+
+ view.setTag(this);
+ }
+ }
+
+ private class ModeAdapter extends BaseAdapter {
+
+ private final LayoutInflater inflater;
+ private final int[] items = {
+ R.string.refresh_default,
+ R.string.refresh_medium,
+ R.string.refresh_high,
+ R.string.refresh_extreme
+ };
+
+ private ModeAdapter(Context context) {
+ inflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public int getCount() {
+ return items.length;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return items[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ TextView view;
+ if (convertView != null) {
+ view = (TextView) convertView;
+ } else {
+ view = (TextView) inflater.inflate(android.R.layout.simple_spinner_dropdown_item,
+ parent, false);
+ }
+
+ view.setText(items[position]);
+ view.setTextSize(14f);
+
+ return view;
+ }
+ }
+
+ private class AllPackagesAdapter extends RecyclerView.Adapter
+ implements AdapterView.OnItemSelectedListener, SectionIndexer {
+
+ private List mEntries = new ArrayList<>();
+ private String[] mSections;
+ private int[] mPositions;
+
+ public AllPackagesAdapter(Context context) {
+ mActivityFilter = new ActivityFilter(context.getPackageManager());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mEntries.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mEntries.get(position).id;
+ }
+@NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new ViewHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.refresh_list_item, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ Context context = holder.itemView.getContext();
+
+ ApplicationsState.AppEntry entry = mEntries.get(position);
+
+ if (entry == null) {
+ return;
+ }
+ holder.mode.setAdapter(new ModeAdapter(context));
+ holder.mode.setOnItemSelectedListener(this);
+ holder.title.setText(entry.label);
+ holder.title.setOnClickListener(v -> holder.mode.performClick());
+ mApplicationsState.ensureIcon(entry);
+ holder.icon.setImageDrawable(entry.icon);
+ int packageState = mRefreshUtils.getStateForPackage(entry.info.packageName);
+ holder.mode.setSelection(packageState, false);
+ holder.mode.setTag(entry);
+ holder.stateIcon.setImageResource(getStateDrawable(packageState));
+ }
+
+ private void setEntries(List entries,
+ List sections, List positions) {
+ mEntries = entries;
+ mSections = sections.toArray(new String[sections.size()]);
+ mPositions = new int[positions.size()];
+ for (int i = 0; i < positions.size(); i++) {
+ mPositions[i] = positions.get(i);
+ }
+ notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ final ApplicationsState.AppEntry entry = (ApplicationsState.AppEntry) parent.getTag();
+
+ int currentState = mRefreshUtils.getStateForPackage(entry.info.packageName);
+ if (currentState != position) {
+ mRefreshUtils.writePackage(entry.info.packageName, position);
+ notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+ }
+
+ @Override
+ public int getPositionForSection(int section) {
+ if (section < 0 || section >= mSections.length) {
+ return -1;
+ }
+
+ return mPositions[section];
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ if (position < 0 || position >= getItemCount()) {
+ return -1;
+ }
+
+ final int index = Arrays.binarySearch(mPositions, position);
+
+ /*
+ * Consider this example: section positions are 0, 3, 5; the supplied
+ * position is 4. The section corresponding to position 4 starts at
+ * position 3, so the expected return value is 1. Binary search will not
+ * find 4 in the array and thus will return -insertPosition-1, i.e. -3.
+ * To get from that number to the expected value of 1 we need to negate
+ * and subtract 2.
+ */
+ return index >= 0 ? index : -index - 2;
+ }
+
+ @Override
+ public Object[] getSections() {
+ return mSections;
+ }
+ }
+
+ private class ActivityFilter implements ApplicationsState.AppFilter {
+
+ private final PackageManager mPackageManager;
+ private final List mLauncherResolveInfoList = new ArrayList();
+
+ private ActivityFilter(PackageManager packageManager) {
+ this.mPackageManager = packageManager;
+
+ updateLauncherInfoList();
+ }
+
+ public void updateLauncherInfoList() {
+ Intent i = new Intent(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_LAUNCHER);
+ List resolveInfoList = mPackageManager.queryIntentActivities(i, 0);
+
+ synchronized (mLauncherResolveInfoList) {
+ mLauncherResolveInfoList.clear();
+ for (ResolveInfo ri : resolveInfoList) {
+ mLauncherResolveInfoList.add(ri.activityInfo.packageName);
+ }
+ }
+ }
+
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(ApplicationsState.AppEntry entry) {
+ boolean show = !mAllPackagesAdapter.mEntries.contains(entry.info.packageName);
+ if (show) {
+ synchronized (mLauncherResolveInfoList) {
+ show = mLauncherResolveInfoList.contains(entry.info.packageName);
+ }
+ }
+ return show;
+ }
+ }
+}
diff --git a/parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java b/parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java
new file mode 100644
index 0000000..cce0a2e
--- /dev/null
+++ b/parts/src/org/lineageos/settings/refreshrate/RefreshUtils.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 The LineageOS 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 org.lineageos.settings.refreshrate;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.UserHandle;
+import android.view.Display;
+
+import android.provider.Settings;
+import androidx.preference.PreferenceManager;
+
+public final class RefreshUtils {
+
+ private static final String REFRESH_CONTROL = "refresh_control";
+ private static final String REFRESH_SERVICE = "refresh_service";
+
+ private static final String KEY_PEAK_REFRESH_RATE = "peak_refresh_rate";
+ private static final String KEY_MIN_REFRESH_RATE = "min_refresh_rate";
+ private Context mContext;
+
+ protected static final int STATE_DEFAULT = 0;
+ protected static final int STATE_STANDARD = 1;
+ protected static final int STATE_HIGH = 2;
+ protected static final int STATE_EXTREME = 3;
+
+ private static final float REFRESH_STATE_DEFAULT = 120f;
+ private static final float REFRESH_STATE_STANDARD = 60f;
+ private static final float REFRESH_STATE_HIGH = 90f;
+ private static final float REFRESH_STATE_EXTREME = 120f;
+
+ private static final String REFRESH_STANDARD = "refresh.standard=";
+ private static final String REFRESH_HIGH = "refresh.high=";
+ private static final String REFRESH_EXTREME = "refresh.extreme=";
+
+ private static boolean isAppInList = false;
+ private static float defaultMaxRate;
+ private static float defaultMinRate;
+
+ private SharedPreferences mSharedPrefs;
+
+ protected RefreshUtils(Context context) {
+ mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+ mContext = context;
+ }
+
+ public static void initialize(Context context) {
+ defaultMaxRate = Settings.System.getFloat(context.getContentResolver(), KEY_PEAK_REFRESH_RATE, REFRESH_STATE_DEFAULT);
+ defaultMinRate = Settings.System.getFloat(context.getContentResolver(), KEY_MIN_REFRESH_RATE, REFRESH_STATE_DEFAULT);
+
+ if (isServiceEnabled(context))
+ startService(context);
+ else
+ setDefaultRefreshRate(context);
+ }
+
+ public static void startService(Context context) {
+ context.startServiceAsUser(new Intent(context, RefreshService.class),
+ UserHandle.CURRENT);
+ PreferenceManager.getDefaultSharedPreferences(context).edit().putString(REFRESH_SERVICE, "true").apply();
+ }
+
+ protected static void stopService(Context context) {
+ context.stopService(new Intent(context, RefreshService.class));
+ PreferenceManager.getDefaultSharedPreferences(context).edit().putString(REFRESH_SERVICE, "false").apply();
+ }
+
+ protected static boolean isServiceEnabled(Context context) {
+ return true;
+ }
+
+ private void writeValue(String profiles) {
+ mSharedPrefs.edit().putString(REFRESH_CONTROL, profiles).apply();
+ }
+
+ private String getValue() {
+ String value = mSharedPrefs.getString(REFRESH_CONTROL, null);
+
+ if (value == null || value.isEmpty()) {
+ value = REFRESH_STANDARD + ":" + REFRESH_HIGH + ":" + REFRESH_EXTREME;
+ writeValue(value);
+ }
+ return value;
+ }
+
+ protected void writePackage(String packageName, int mode) {
+ String value = getValue();
+ value = value.replace(packageName + ",", "");
+ String[] modes = value.split(":");
+ String finalString;
+
+ switch (mode) {
+ case STATE_STANDARD:
+ modes[0] = modes[0] + packageName + ",";
+ break;
+ case STATE_HIGH:
+ modes[1] = modes[1] + packageName + ",";
+ break;
+ case STATE_EXTREME:
+ modes[2] = modes[2] + packageName + ",";
+ break;
+ }
+
+ finalString = modes[0] + ":" + modes[1] + ":" + modes[2];
+
+ writeValue(finalString);
+ }
+
+ protected int getStateForPackage(String packageName) {
+ String value = getValue();
+ String[] modes = value.split(":");
+ int state = STATE_DEFAULT;
+ if (modes[0].contains(packageName + ",")) {
+ state = STATE_STANDARD;
+ } else if (modes[1].contains(packageName + ",")) {
+ state = STATE_HIGH;
+ } else if (modes[2].contains(packageName + ",")) {
+ state = STATE_EXTREME;
+ }
+ return state;
+ }
+
+ protected static void setDefaultRefreshRate(Context context) {
+ Settings.System.putFloat(context.getContentResolver(), KEY_PEAK_REFRESH_RATE, defaultMaxRate);
+ Settings.System.putFloat(context.getContentResolver(), KEY_MIN_REFRESH_RATE, defaultMinRate);
+ }
+
+ protected void setRefreshRate(String packageName) {
+ String value = getValue();
+ String modes[];
+
+ if (!isAppInList) {
+ defaultMaxRate = Settings.System.getFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, REFRESH_STATE_DEFAULT);
+ defaultMinRate = Settings.System.getFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, REFRESH_STATE_DEFAULT);
+ }
+
+ float minrate = defaultMinRate;
+ float maxrate = defaultMaxRate;
+
+ if (value != null) {
+ modes = value.split(":");
+ if (modes[0].contains(packageName + ",")) {
+ maxrate = REFRESH_STATE_STANDARD;
+ if (minrate > maxrate) {
+ minrate = maxrate;
+ }
+ isAppInList = true;
+ } else if (modes[1].contains(packageName + ",")) {
+ maxrate = REFRESH_STATE_HIGH;
+ if (minrate > maxrate) {
+ minrate = maxrate;
+ }
+ } else if (modes[2].contains(packageName + ",")) {
+ maxrate = REFRESH_STATE_EXTREME;
+ if (minrate > maxrate) {
+ minrate = maxrate;
+ }
+ isAppInList = true;
+ } else {
+ isAppInList = false;
+ }
+ }
+ Settings.System.putFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, maxrate);
+ Settings.System.putFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, minrate);
+ }
+}
diff --git a/parts/src/org/lineageos/settings/speaker/ClearSpeakerActivity.java b/parts/src/org/lineageos/settings/speaker/ClearSpeakerActivity.java
new file mode 100644
index 0000000..d1e74a4
--- /dev/null
+++ b/parts/src/org/lineageos/settings/speaker/ClearSpeakerActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 Paranoid Android
+ *
+ * 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 org.lineageos.settings.speaker;
+
+import android.os.Bundle;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+import com.android.settingslib.widget.R;
+
+public class ClearSpeakerActivity extends CollapsingToolbarBaseActivity {
+
+ private static final String TAG_CLEARSPEAKER = "clearspeaker";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getFragmentManager().beginTransaction().replace(R.id.content_frame,
+ new ClearSpeakerFragment(), TAG_CLEARSPEAKER).commit();
+ }
+}
diff --git a/parts/src/org/lineageos/settings/speaker/ClearSpeakerFragment.java b/parts/src/org/lineageos/settings/speaker/ClearSpeakerFragment.java
new file mode 100644
index 0000000..4dad735
--- /dev/null
+++ b/parts/src/org/lineageos/settings/speaker/ClearSpeakerFragment.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 Paranoid Android
+ *
+ * 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 org.lineageos.settings.speaker;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioManager;
+import android.media.AudioAttributes;
+import android.media.MediaPlayer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.SwitchPreference;
+
+import org.lineageos.settings.R;
+
+import java.io.IOException;
+
+public class ClearSpeakerFragment extends PreferenceFragment implements
+ Preference.OnPreferenceChangeListener {
+
+ private static final String TAG = ClearSpeakerFragment.class.getSimpleName();
+
+ private static final String PREF_CLEAR_SPEAKER = "clear_speaker_pref";
+
+ private AudioManager mAudioManager;
+ private Handler mHandler;
+ private MediaPlayer mMediaPlayer;
+ private SwitchPreference mClearSpeakerPref;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.clear_speaker_settings);
+
+ mClearSpeakerPref = (SwitchPreference) findPreference(PREF_CLEAR_SPEAKER);
+ mClearSpeakerPref.setOnPreferenceChangeListener(this);
+
+ mHandler = new Handler();
+ mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (preference == mClearSpeakerPref) {
+ boolean value = (Boolean) newValue;
+ if (value) {
+ if (startPlaying()) {
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.postDelayed(() -> {
+ stopPlaying();
+ }, 30000);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ stopPlaying();
+ }
+
+ public boolean startPlaying() {
+ mAudioManager.setParameters("status_earpiece_clean=on");
+ mMediaPlayer = new MediaPlayer();
+ getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC);
+ mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mMediaPlayer.setLooping(true);
+ try {
+ AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.clear_speaker_sound);
+ try {
+ mMediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
+ } finally {
+ file.close();
+ }
+ mClearSpeakerPref.setEnabled(false);
+ mMediaPlayer.setVolume(1.0f, 1.0f);
+ mMediaPlayer.prepare();
+ mMediaPlayer.start();
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to play speaker clean sound!", ioe);
+ return false;
+ }
+ return true;
+ }
+
+ public void stopPlaying() {
+ if (mMediaPlayer != null) {
+ if (mMediaPlayer.isPlaying()) {
+ mMediaPlayer.stop();
+ mMediaPlayer.reset();
+ mMediaPlayer.release();
+ mMediaPlayer=null;
+ }
+ }
+ mAudioManager.setParameters("status_earpiece_clean=off");
+ mClearSpeakerPref.setEnabled(true);
+ mClearSpeakerPref.setChecked(false);
+ }
+}
diff --git a/parts/src/org/lineageos/settings/thermal/ThermalActivity.java b/parts/src/org/lineageos/settings/thermal/ThermalActivity.java
new file mode 100644
index 0000000..cb02489
--- /dev/null
+++ b/parts/src/org/lineageos/settings/thermal/ThermalActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020,2022 The LineageOS 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 org.lineageos.settings.thermal;
+
+import android.os.Bundle;
+
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
+import com.android.settingslib.widget.R;
+
+public class ThermalActivity extends CollapsingToolbarBaseActivity {
+ private static final String TAG_THERMAL = "thermal";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getFragmentManager().beginTransaction().replace(R.id.content_frame,
+ new ThermalSettingsFragment(), TAG_THERMAL).commit();
+ }
+}
diff --git a/parts/src/org/lineageos/settings/thermal/ThermalService.java b/parts/src/org/lineageos/settings/thermal/ThermalService.java
new file mode 100644
index 0000000..cd8e1c1
--- /dev/null
+++ b/parts/src/org/lineageos/settings/thermal/ThermalService.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The LineageOS 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 org.lineageos.settings.thermal;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.TaskStackListener;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+public class ThermalService extends Service {
+
+ private static final String TAG = "ThermalService";
+ private static final boolean DEBUG = false;
+
+ private String mPreviousApp;
+ private ThermalUtils mThermalUtils;
+
+ @Override
+ public void onCreate() {
+ if (DEBUG) Log.d(TAG, "Creating service");
+ try {
+ ActivityTaskManager.getService().registerTaskStackListener(mTaskListener);
+ } catch (RemoteException e) {
+ // Do nothing
+ }
+ mThermalUtils = new ThermalUtils(this);
+ super.onCreate();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (DEBUG) Log.d(TAG, "Starting service");
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ private final TaskStackListener mTaskListener = new TaskStackListener() {
+ @Override
+ public void onTaskStackChanged() {
+ try {
+ final ActivityTaskManager.RootTaskInfo focusedTask =
+ ActivityTaskManager.getService().getFocusedRootTaskInfo();
+ if (focusedTask != null && focusedTask.topActivity != null) {
+ ComponentName taskComponentName = focusedTask.topActivity;
+ String foregroundApp = taskComponentName.getPackageName();
+ if (DEBUG) Log.d(TAG, "onTaskStackChanged: foregroundApp=" + foregroundApp);
+ if (!foregroundApp.equals(mPreviousApp)) {
+ mThermalUtils.setThermalProfile(foregroundApp);
+ mPreviousApp = foregroundApp;
+ }
+ }
+ } catch (Exception e) {}
+ }
+ };
+}
diff --git a/parts/src/org/lineageos/settings/thermal/ThermalSettingsFragment.java b/parts/src/org/lineageos/settings/thermal/ThermalSettingsFragment.java
new file mode 100644
index 0000000..7618b94
--- /dev/null
+++ b/parts/src/org/lineageos/settings/thermal/ThermalSettingsFragment.java
@@ -0,0 +1,428 @@
+/**
+ * Copyright (C) 2020 The LineageOS 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 org.lineageos.settings.thermal;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.preference.PreferenceFragment;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.android.settingslib.applications.ApplicationsState;
+
+import org.lineageos.settings.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ThermalSettingsFragment extends PreferenceFragment
+ implements ApplicationsState.Callbacks {
+
+ private AllPackagesAdapter mAllPackagesAdapter;
+ private ApplicationsState mApplicationsState;
+ private ApplicationsState.Session mSession;
+ private ActivityFilter mActivityFilter;
+ private Map mEntryMap =
+ new HashMap();
+
+ private RecyclerView mAppsRecyclerView;
+ private ThermalUtils mThermalUtils;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
+ mSession = mApplicationsState.newSession(this);
+ mSession.onResume();
+ mActivityFilter = new ActivityFilter(getActivity().getPackageManager());
+
+ mAllPackagesAdapter = new AllPackagesAdapter(getActivity());
+
+ mThermalUtils = new ThermalUtils(getActivity());
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.thermal_layout, container, false);
+ }
+
+ @Override
+ public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ mAppsRecyclerView = view.findViewById(R.id.thermal_rv_view);
+ mAppsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+ mAppsRecyclerView.setAdapter(mAllPackagesAdapter);
+ }
+
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ rebuild();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ mSession.onPause();
+ mSession.onDestroy();
+ }
+
+ @Override
+ public void onPackageListChanged() {
+ mActivityFilter.updateLauncherInfoList();
+ rebuild();
+ }
+
+ @Override
+ public void onRebuildComplete(ArrayList entries) {
+ if (entries != null) {
+ handleAppEntries(entries);
+ mAllPackagesAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onLoadEntriesCompleted() {
+ rebuild();
+ }
+
+ @Override
+ public void onAllSizesComputed() {
+ }
+
+ @Override
+ public void onLauncherInfoChanged() {
+ }
+
+ @Override
+ public void onPackageIconChanged() {
+ }
+
+ @Override
+ public void onPackageSizeChanged(String packageName) {
+ }
+
+ @Override
+ public void onRunningStateChanged(boolean running) {
+ }
+
+ private void handleAppEntries(List entries) {
+ final ArrayList sections = new ArrayList();
+ final ArrayList positions = new ArrayList();
+ final PackageManager pm = getActivity().getPackageManager();
+ String lastSectionIndex = null;
+ int offset = 0;
+
+ for (int i = 0; i < entries.size(); i++) {
+ final ApplicationInfo info = entries.get(i).info;
+ final String label = (String) info.loadLabel(pm);
+ final String sectionIndex;
+
+ if (!info.enabled) {
+ sectionIndex = "--"; // XXX
+ } else if (TextUtils.isEmpty(label)) {
+ sectionIndex = "";
+ } else {
+ sectionIndex = label.substring(0, 1).toUpperCase();
+ }
+
+ if (lastSectionIndex == null ||
+ !TextUtils.equals(sectionIndex, lastSectionIndex)) {
+ sections.add(sectionIndex);
+ positions.add(offset);
+ lastSectionIndex = sectionIndex;
+ }
+
+ offset++;
+ }
+
+ mAllPackagesAdapter.setEntries(entries, sections, positions);
+ mEntryMap.clear();
+ for (ApplicationsState.AppEntry e : entries) {
+ mEntryMap.put(e.info.packageName, e);
+ }
+ }
+
+ private void rebuild() {
+ mSession.rebuild(mActivityFilter, ApplicationsState.ALPHA_COMPARATOR);
+ }
+
+ private int getStateDrawable(int state) {
+ switch (state) {
+ case ThermalUtils.STATE_BENCHMARK:
+ return R.drawable.ic_thermal_benchmark;
+ case ThermalUtils.STATE_BROWSER:
+ return R.drawable.ic_thermal_browser;
+ case ThermalUtils.STATE_CAMERA:
+ return R.drawable.ic_thermal_camera;
+ case ThermalUtils.STATE_DIALER:
+ return R.drawable.ic_thermal_dialer;
+ case ThermalUtils.STATE_GAMING:
+ return R.drawable.ic_thermal_gaming;
+ case ThermalUtils.STATE_STREAMING:
+ return R.drawable.ic_thermal_streaming;
+ case ThermalUtils.STATE_DEFAULT:
+ default:
+ return R.drawable.ic_thermal_default;
+ }
+ }
+
+ private class ViewHolder extends RecyclerView.ViewHolder {
+ private TextView title;
+ private Spinner mode;
+ private ImageView icon;
+ private View rootView;
+ private ImageView stateIcon;
+
+ private ViewHolder(View view) {
+ super(view);
+ this.title = view.findViewById(R.id.app_name);
+ this.mode = view.findViewById(R.id.app_mode);
+ this.icon = view.findViewById(R.id.app_icon);
+ this.stateIcon = view.findViewById(R.id.state);
+ this.rootView = view;
+
+ view.setTag(this);
+ }
+ }
+
+ private class ModeAdapter extends BaseAdapter {
+
+ private final LayoutInflater inflater;
+ private final int[] items = {
+ R.string.thermal_default,
+ R.string.thermal_benchmark,
+ R.string.thermal_browser,
+ R.string.thermal_camera,
+ R.string.thermal_dialer,
+ R.string.thermal_gaming,
+ R.string.thermal_streaming
+ };
+
+ private ModeAdapter(Context context) {
+ inflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public int getCount() {
+ return items.length;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return items[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ TextView view;
+ if (convertView != null) {
+ view = (TextView) convertView;
+ } else {
+ view = (TextView) inflater.inflate(android.R.layout.simple_spinner_dropdown_item,
+ parent, false);
+ }
+
+ view.setText(items[position]);
+ view.setTextSize(14f);
+ return view;
+ }
+ }
+
+ private class AllPackagesAdapter extends RecyclerView.Adapter
+ implements AdapterView.OnItemSelectedListener, SectionIndexer {
+
+ private List mEntries = new ArrayList<>();
+ private String[] mSections;
+ private int[] mPositions;
+
+ public AllPackagesAdapter(Context context) {
+ mActivityFilter = new ActivityFilter(context.getPackageManager());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mEntries.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mEntries.get(position).id;
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new ViewHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.thermal_list_item, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ Context context = holder.itemView.getContext();
+ ApplicationsState.AppEntry entry = mEntries.get(position);
+ if (entry == null) {
+ return;
+ }
+
+ holder.mode.setAdapter(new ModeAdapter(context));
+ holder.mode.setOnItemSelectedListener(this);
+ holder.title.setText(entry.label);
+ holder.title.setOnClickListener(v -> holder.mode.performClick());
+ mApplicationsState.ensureIcon(entry);
+ holder.icon.setImageDrawable(entry.icon);
+ int packageState = mThermalUtils.getStateForPackage(entry.info.packageName);
+ holder.mode.setSelection(packageState, false);
+ holder.mode.setTag(entry);
+ holder.stateIcon.setImageResource(getStateDrawable(packageState));
+ }
+
+ private void setEntries(List entries,
+ List sections, List positions) {
+ mEntries = entries;
+ mSections = sections.toArray(new String[sections.size()]);
+ mPositions = new int[positions.size()];
+ for (int i = 0; i < positions.size(); i++) {
+ mPositions[i] = positions.get(i);
+ }
+ notifyDataSetChanged();
+ }
+
+
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ final ApplicationsState.AppEntry entry = (ApplicationsState.AppEntry) parent.getTag();
+ int currentState = mThermalUtils.getStateForPackage(entry.info.packageName);
+ if (currentState != position) {
+ mThermalUtils.writePackage(entry.info.packageName, position);
+ notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+ }
+
+ @Override
+ public int getPositionForSection(int section) {
+ if (section < 0 || section >= mSections.length) {
+ return -1;
+ }
+
+ return mPositions[section];
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ if (position < 0 || position >= getItemCount()) {
+ return -1;
+ }
+
+ final int index = Arrays.binarySearch(mPositions, position);
+
+ /*
+ * Consider this example: section positions are 0, 3, 5; the supplied
+ * position is 4. The section corresponding to position 4 starts at
+ * position 3, so the expected return value is 1. Binary search will not
+ * find 4 in the array and thus will return -insertPosition-1, i.e. -3.
+ * To get from that number to the expected value of 1 we need to negate
+ * and subtract 2.
+ */
+ return index >= 0 ? index : -index - 2;
+ }
+
+ @Override
+ public Object[] getSections() {
+ return mSections;
+ }
+ }
+
+ private class ActivityFilter implements ApplicationsState.AppFilter {
+
+ private final PackageManager mPackageManager;
+ private final List mLauncherResolveInfoList = new ArrayList();
+
+ private ActivityFilter(PackageManager packageManager) {
+ this.mPackageManager = packageManager;
+
+ updateLauncherInfoList();
+ }
+
+ public void updateLauncherInfoList() {
+ Intent i = new Intent(Intent.ACTION_MAIN);
+ i.addCategory(Intent.CATEGORY_LAUNCHER);
+ List resolveInfoList = mPackageManager.queryIntentActivities(i, 0);
+
+ synchronized (mLauncherResolveInfoList) {
+ mLauncherResolveInfoList.clear();
+ for (ResolveInfo ri : resolveInfoList) {
+ mLauncherResolveInfoList.add(ri.activityInfo.packageName);
+ }
+ }
+ }
+
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(ApplicationsState.AppEntry entry) {
+ boolean show = !mAllPackagesAdapter.mEntries.contains(entry.info.packageName);
+ if (show) {
+ synchronized (mLauncherResolveInfoList) {
+ show = mLauncherResolveInfoList.contains(entry.info.packageName);
+ }
+ }
+ return show;
+ }
+ }
+}
diff --git a/parts/src/org/lineageos/settings/thermal/ThermalUtils.java b/parts/src/org/lineageos/settings/thermal/ThermalUtils.java
new file mode 100644
index 0000000..fa538b0
--- /dev/null
+++ b/parts/src/org/lineageos/settings/thermal/ThermalUtils.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The LineageOS 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 org.lineageos.settings.thermal;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.UserHandle;
+
+import androidx.preference.PreferenceManager;
+
+import org.lineageos.settings.utils.FileUtils;
+
+public final class ThermalUtils {
+
+ private static final String THERMAL_CONTROL = "thermal_control";
+
+ protected static final int STATE_DEFAULT = 0;
+ protected static final int STATE_BENCHMARK = 1;
+ protected static final int STATE_BROWSER = 2;
+ protected static final int STATE_CAMERA = 3;
+ protected static final int STATE_DIALER = 4;
+ protected static final int STATE_GAMING = 5;
+ protected static final int STATE_STREAMING = 6;
+
+ private static final String THERMAL_STATE_DEFAULT = "0";
+ private static final String THERMAL_STATE_BENCHMARK = "10";
+ private static final String THERMAL_STATE_BROWSER = "11";
+ private static final String THERMAL_STATE_CAMERA = "12";
+ private static final String THERMAL_STATE_DIALER = "8";
+ private static final String THERMAL_STATE_GAMING = "9";
+ private static final String THERMAL_STATE_STREAMING = "14";
+
+ private static final String THERMAL_BENCHMARK = "thermal.benchmark=";
+ private static final String THERMAL_BROWSER = "thermal.browser=";
+ private static final String THERMAL_CAMERA = "thermal.camera=";
+ private static final String THERMAL_DIALER = "thermal.dialer=";
+ private static final String THERMAL_GAMING = "thermal.gaming=";
+ private static final String THERMAL_STREAMING = "thermal.streaming=";
+
+ private static final String THERMAL_SCONFIG = "/sys/class/thermal/thermal_message/sconfig";
+
+ private SharedPreferences mSharedPrefs;
+
+ protected ThermalUtils(Context context) {
+ mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+ }
+
+ public static void startService(Context context) {
+ context.startServiceAsUser(new Intent(context, ThermalService.class),
+ UserHandle.CURRENT);
+ }
+
+ private void writeValue(String profiles) {
+ mSharedPrefs.edit().putString(THERMAL_CONTROL, profiles).apply();
+ }
+
+ private String getValue() {
+ String value = mSharedPrefs.getString(THERMAL_CONTROL, null);
+
+ if (value == null || value.isEmpty()) {
+ value = THERMAL_BENCHMARK + ":" + THERMAL_BROWSER + ":" + THERMAL_CAMERA + ":" +
+ THERMAL_DIALER + ":" + THERMAL_GAMING + ":" + THERMAL_STREAMING;
+ writeValue(value);
+ }
+ return value;
+ }
+
+ protected void writePackage(String packageName, int mode) {
+ String value = getValue();
+ value = value.replace(packageName + ",", "");
+ String[] modes = value.split(":");
+ String finalString;
+
+ switch (mode) {
+ case STATE_BENCHMARK:
+ modes[0] = modes[0] + packageName + ",";
+ break;
+ case STATE_BROWSER:
+ modes[1] = modes[1] + packageName + ",";
+ break;
+ case STATE_CAMERA:
+ modes[2] = modes[2] + packageName + ",";
+ break;
+ case STATE_DIALER:
+ modes[3] = modes[3] + packageName + ",";
+ break;
+ case STATE_GAMING:
+ modes[4] = modes[4] + packageName + ",";
+ break;
+ case STATE_STREAMING:
+ modes[5] = modes[5] + packageName + ",";
+ break;
+ }
+
+ finalString = modes[0] + ":" + modes[1] + ":" + modes[2] + ":" + modes[3] + ":" +
+ modes[4] + ":" + modes[5];
+
+ writeValue(finalString);
+ }
+
+ protected int getStateForPackage(String packageName) {
+ String value = getValue();
+ String[] modes = value.split(":");
+ int state = STATE_DEFAULT;
+ if (modes[0].contains(packageName + ",")) {
+ state = STATE_BENCHMARK;
+ } else if (modes[1].contains(packageName + ",")) {
+ state = STATE_BROWSER;
+ } else if (modes[2].contains(packageName + ",")) {
+ state = STATE_CAMERA;
+ } else if (modes[3].contains(packageName + ",")) {
+ state = STATE_DIALER;
+ } else if (modes[4].contains(packageName + ",")) {
+ state = STATE_GAMING;
+ } else if (modes[5].contains(packageName + ",")) {
+ state = STATE_STREAMING;
+ }
+
+ return state;
+ }
+
+ protected void setThermalProfile(String packageName) {
+ String value = getValue();
+ String modes[];
+ String state = THERMAL_STATE_DEFAULT;
+
+ if (value != null) {
+ modes = value.split(":");
+
+ if (modes[0].contains(packageName + ",")) {
+ state = THERMAL_STATE_BENCHMARK;
+ } else if (modes[1].contains(packageName + ",")) {
+ state = THERMAL_STATE_BROWSER;
+ } else if (modes[2].contains(packageName + ",")) {
+ state = THERMAL_STATE_CAMERA;
+ } else if (modes[3].contains(packageName + ",")) {
+ state = THERMAL_STATE_DIALER;
+ } else if (modes[4].contains(packageName + ",")) {
+ state = THERMAL_STATE_GAMING;
+ } else if (modes[5].contains(packageName + ",")) {
+ state = THERMAL_STATE_STREAMING;
+ }
+ }
+ FileUtils.writeLine(THERMAL_SCONFIG, state);
+ }
+}
diff --git a/parts/src/org/lineageos/settings/utils/FileUtils.java b/parts/src/org/lineageos/settings/utils/FileUtils.java
new file mode 100644
index 0000000..00028ff
--- /dev/null
+++ b/parts/src/org/lineageos/settings/utils/FileUtils.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 org.lineageos.settings.utils;
+
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public final class FileUtils {
+ private static final String TAG = "FileUtils";
+
+ private FileUtils() {
+ // This class is not supposed to be instantiated
+ }
+
+ /**
+ * Reads the first line of text from the given file.
+ * Reference {@link BufferedReader#readLine()} for clarification on what a line is
+ *
+ * @return the read line contents, or null on failure
+ */
+ public static String readOneLine(String fileName) {
+ String line = null;
+ BufferedReader reader = null;
+
+ try {
+ reader = new BufferedReader(new FileReader(fileName), 512);
+ line = reader.readLine();
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "No such file " + fileName + " for reading", e);
+ } catch (IOException e) {
+ Log.e(TAG, "Could not read from file " + fileName, e);
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ } catch (IOException e) {
+ // Ignored, not much we can do anyway
+ }
+ }
+
+ return line;
+ }
+
+ /**
+ * Writes the given value into the given file
+ *
+ * @return true on success, false on failure
+ */
+ public static boolean writeLine(String fileName, String value) {
+ BufferedWriter writer = null;
+
+ try {
+ writer = new BufferedWriter(new FileWriter(fileName));
+ writer.write(value);
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "No such file " + fileName + " for writing", e);
+ return false;
+ } catch (IOException e) {
+ Log.e(TAG, "Could not write to file " + fileName, e);
+ return false;
+ } finally {
+ try {
+ if (writer != null) {
+ writer.close();
+ }
+ } catch (IOException e) {
+ // Ignored, not much we can do anyway
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks whether the given file exists
+ *
+ * @return true if exists, false if not
+ */
+ public static boolean fileExists(String fileName) {
+ final File file = new File(fileName);
+ return file.exists();
+ }
+
+ /**
+ * Checks whether the given file is readable
+ *
+ * @return true if readable, false if not
+ */
+ public static boolean isFileReadable(String fileName) {
+ final File file = new File(fileName);
+ return file.exists() && file.canRead();
+ }
+
+ /**
+ * Checks whether the given file is writable
+ *
+ * @return true if writable, false if not
+ */
+ public static boolean isFileWritable(String fileName) {
+ final File file = new File(fileName);
+ return file.exists() && file.canWrite();
+ }
+
+ /**
+ * Deletes an existing file
+ *
+ * @return true if the delete was successful, false if not
+ */
+ public static boolean delete(String fileName) {
+ final File file = new File(fileName);
+ boolean ok = false;
+ try {
+ ok = file.delete();
+ } catch (SecurityException e) {
+ Log.w(TAG, "SecurityException trying to delete " + fileName, e);
+ }
+ return ok;
+ }
+
+ /**
+ * Renames an existing file
+ *
+ * @return true if the rename was successful, false if not
+ */
+ public static boolean rename(String srcPath, String dstPath) {
+ final File srcFile = new File(srcPath);
+ final File dstFile = new File(dstPath);
+ boolean ok = false;
+ try {
+ ok = srcFile.renameTo(dstFile);
+ } catch (SecurityException e) {
+ Log.w(TAG, "SecurityException trying to rename " + srcPath + " to " + dstPath, e);
+ } catch (NullPointerException e) {
+ Log.e(TAG, "NullPointerException trying to rename " + srcPath + " to " + dstPath, e);
+ }
+ return ok;
+ }
+}
diff --git a/rootdir/etc/init.target.rc b/rootdir/etc/init.target.rc
index 73a28ee..990acaa 100644
--- a/rootdir/etc/init.target.rc
+++ b/rootdir/etc/init.target.rc
@@ -152,7 +152,10 @@ on boot
setprop vendor.usb.controller 4e00000.dwc3
#ExtR HONGMI-90116,wufa@wingtech.com,add,20210908,add mi_thermal
+ chmod 0664 /sys/class/thermal/thermal_message/sconfig
chown system system /sys/class/thermal/thermal_message/sconfig
+ chmod 0666 /sys/class/thermal/thermal_message/temp_state
+ chown system system /sys/class/thermal/thermal_message/temp_state
# add dual
mkdir /mnt/vendor/persist/camera 0777 system system
diff --git a/sepolicy/private/devicesettings_app.te b/sepolicy/private/devicesettings_app.te
new file mode 100644
index 0000000..2f6a3d0
--- /dev/null
+++ b/sepolicy/private/devicesettings_app.te
@@ -0,0 +1,28 @@
+app_domain(devicesettings_app)
+
+# Allow devicesettings_app to find *_service
+allow devicesettings_app {
+ app_api_service
+ audioserver_service
+ cameraserver_service
+ drmserver_service
+ mediaextractor_service
+ mediametrics_service
+ mediaserver_service
+}:service_manager find;
+
+
+# Allow devicesettings_app read and write /data/data subdirectory
+allow devicesettings_app system_app_data_file:dir create_dir_perms;
+allow devicesettings_app system_app_data_file:{ file lnk_file } create_file_perms;
+
+# Allow binder communication with gpuservice
+binder_call(devicesettings_app, gpuservice)
+
+# Allow devicesettings_app to read and write to cgroup/sysfs_leds/sysfs_thermal
+allow devicesettings_app sysfs_leds:dir search;
+allow devicesettings_app {
+ cgroup
+ sysfs_leds
+ sysfs_thermal
+}:{ file lnk_file } rw_file_perms;
diff --git a/sepolicy/private/property.te b/sepolicy/private/property.te
new file mode 100644
index 0000000..27d0385
--- /dev/null
+++ b/sepolicy/private/property.te
@@ -0,0 +1,2 @@
+# SettingsLib
+system_public_prop(settingslib_prop)
diff --git a/sepolicy/private/property_contexts b/sepolicy/private/property_contexts
index 05840c9..459b39f 100644
--- a/sepolicy/private/property_contexts
+++ b/sepolicy/private/property_contexts
@@ -37,3 +37,9 @@ wifi.pktlog.debug.0.chen u:object_r:exported_system_prop:s0
#
ro.factory_mode u:object_r:exported_default_prop:s0
+
+# SettingsLib
+settingsdebug.instant.packages u:object_r:settingslib_prop:s0
+
+# Camera
+vendor.camera.aux.packagelist u:object_r:vendor_camera_prop:s0
diff --git a/sepolicy/private/system_app.te b/sepolicy/private/system_app.te
index 5fae7c8..c2e3218 100644
--- a/sepolicy/private/system_app.te
+++ b/sepolicy/private/system_app.te
@@ -5,3 +5,6 @@ allow system_app sysfs_zram:file r_file_perms;
# Allow settings to query qemu.hw.mainkeys
get_prop(system_app, qemu_hw_prop)
+
+# Allow System Apps to get settingsdebug.instant.packages prop
+get_prop(system_app, settingslib_prop)
diff --git a/sepolicy/public/devicesettings_app.te b/sepolicy/public/devicesettings_app.te
new file mode 100644
index 0000000..9c05e95
--- /dev/null
+++ b/sepolicy/public/devicesettings_app.te
@@ -0,0 +1,2 @@
+type devicesettings_app, domain;
+typeattribute devicesettings_app mlstrustedsubject;
diff --git a/sepolicy/vendor/devicesettings_app.te b/sepolicy/vendor/devicesettings_app.te
new file mode 100644
index 0000000..34aa534
--- /dev/null
+++ b/sepolicy/vendor/devicesettings_app.te
@@ -0,0 +1,9 @@
+allow devicesettings_app vendor_sysfs_graphics:dir search;
+allow devicesettings_app vendor_sysfs_graphics:file rw_file_perms;
+
+allow devicesettings_app vendor_sysfs_kgsl:dir search;
+
+allow devicesettings_app vendor_sysfs_kgsl:{ file lnk_file } rw_file_perms;
+
+allow devicesettings_app vendor_sysfs_graphics:file write;
+r_dir_file(devicesettings_app, vendor_sysfs_graphics)
diff --git a/sepolicy/vendor/hal_fingerprint_default.te b/sepolicy/vendor/hal_fingerprint_default.te
index 4704107..58c43a3 100644
--- a/sepolicy/vendor/hal_fingerprint_default.te
+++ b/sepolicy/vendor/hal_fingerprint_default.te
@@ -8,6 +8,9 @@ allow hal_fingerprint_default vendor_hal_perf_hwservice:hwservice_manager find;
allow hal_fingerprint_default fingerprint_data_file:dir rw_dir_perms;
allow hal_fingerprint_default fingerprint_data_file:file create_file_perms;
+allow hal_fingerprint_default system_prop:property_service { set };
+allow hal_fingerprint_default exported_system_prop:property_service { set };
+
# Dev nodes
allow hal_fingerprint_default {
fingerprint_device
diff --git a/sepolicy/vendor/hal_sensors_default.te b/sepolicy/vendor/hal_sensors_default.te
index 4b53b97..4960ff7 100644
--- a/sepolicy/vendor/hal_sensors_default.te
+++ b/sepolicy/vendor/hal_sensors_default.te
@@ -4,6 +4,8 @@ hal_client_domain(hal_sensors_default, hal_audio)
allow hal_sensors_default hal_sensors_default:qipcrtr_socket { ioctl };
+allow hal_sensors_default sysfs:file { read write };
+
allow hal_sensors_default audio_socket:sock_file rw_file_perms;
allow hal_sensors_default socket_device:sock_file rw_file_perms;
allow hal_sensors_default sound_device:chr_file rw_file_perms;
diff --git a/sepolicy/vendor/stflashtool.te b/sepolicy/vendor/stflashtool.te
index a5feae0..62a8c63 100644
--- a/sepolicy/vendor/stflashtool.te
+++ b/sepolicy/vendor/stflashtool.te
@@ -8,3 +8,5 @@ allow stflashtool nfc_device:chr_file {ioctl read write getattr lock append map
get_prop(stflashtool, vendor_radio_prop)
get_prop(stflashtool, vendor_nfc_prop)
set_prop(stflashtool, vendor_nfc_prop)
+
+allow stflashtool nfc_prop:file { read };
diff --git a/sepolicy/vendor/system_app.te b/sepolicy/vendor/system_app.te
index 4a4a48b..049faaa 100644
--- a/sepolicy/vendor/system_app.te
+++ b/sepolicy/vendor/system_app.te
@@ -18,3 +18,7 @@ allow system_app vendor_sysfs_battery_supply:dir { search };
allow system_app vendor_sysfs_battery_supply:file { getattr open read };
r_dir_file(system_app, vendor_sysfs_battery_supply)
+
+allow system_app sysfs:file { setattr getattr write open read };
+allow system_app vendor_sysfs_graphics:dir { search setattr getattr write open read };
+allow system_app privapp_data_file:dir { search };
diff --git a/sepolicy/vendor/system_server.te b/sepolicy/vendor/system_server.te
index 0499c24..1ef38f2 100644
--- a/sepolicy/vendor/system_server.te
+++ b/sepolicy/vendor/system_server.te
@@ -10,6 +10,7 @@ allow system_server system_file:file r_file_perms;
# Allow system_server to set vendor_persist_camera_prop
get_prop(system_server, vendor_camera_prop)
get_prop(system_server,vendor_persist_camera_prop)
+allow system_server vendor_camera_prop:file { read open };
# Allow system_server to read Fast Charging status
allow system_server vendor_sysfs_battery_supply:file { getattr open read };
diff --git a/sepolicy/vendor/untrusted_app.te b/sepolicy/vendor/untrusted_app.te
index cd9be2a..9130c0a 100644
--- a/sepolicy/vendor/untrusted_app.te
+++ b/sepolicy/vendor/untrusted_app.te
@@ -4,3 +4,14 @@ allow untrusted_app proc_zoneinfo:file r_file_perms;
allow untrusted_app tmpfs:lnk_file { read };
allow untrusted_app shell_test_data_file:dir { search };
allow untrusted_app app_data_file:file { execute };
+
+allow untrusted_app mnt_vendor_file:dir { search };
+allow untrusted_app mnt_vendor_file:dir { getattr };
+allow untrusted_app block_device:dir { search };
+allow untrusted_app proc_overcommit_memory:file { read };
+
+allow untrusted_app proc_tty_drivers:file { read };
+allow untrusted_app qemu_hw_prop:file { read };
+allow untrusted_app qemu_sf_lcd_density_prop:file { read };
+allow untrusted_app serialno_prop:file { read };
+allow untrusted_app proc_max_map_count:file { read };
diff --git a/sepolicy/vendor/vendor_hal_gnss_qti.te b/sepolicy/vendor/vendor_hal_gnss_qti.te
new file mode 100644
index 0000000..eb2c9d0
--- /dev/null
+++ b/sepolicy/vendor/vendor_hal_gnss_qti.te
@@ -0,0 +1 @@
+allow vendor_hal_gnss_qti system_prop:file { read };
diff --git a/sepolicy/vendor/vendor_hvdcp.te b/sepolicy/vendor/vendor_hvdcp.te
index 034fb14..69015ca 100644
--- a/sepolicy/vendor/vendor_hvdcp.te
+++ b/sepolicy/vendor/vendor_hvdcp.te
@@ -1 +1,3 @@
allow vendor_hvdcp kmsg_device:chr_file rw_file_perms;
+
+allow vendor_hvdcp vendor_sysfs_iio:dir { read };
diff --git a/sepolicy/vendor/vendor_qti_init_shell.te b/sepolicy/vendor/vendor_qti_init_shell.te
index 9a61c4e..e972ffe 100644
--- a/sepolicy/vendor/vendor_qti_init_shell.te
+++ b/sepolicy/vendor/vendor_qti_init_shell.te
@@ -9,3 +9,5 @@ allow vendor_qti_init_shell sysfs_wakeup:file setattr;
allow vendor_qti_init_shell sysfs:file { setattr write };
allow vendor_qti_init_shell proc_watermark_scale_factor:file w_file_perms;
allow vendor_qti_init_shell proc_watermark_boost_factor:file w_file_perms;
+
+allow vendor_qti_init_shell vendor_sysfs_qdss_dev:file { setattr };