From ad4bce40b290bc7246d7c4bce8ce2998ff69c730 Mon Sep 17 00:00:00 2001 From: Gegham Zakaryan Date: Sun, 4 Apr 2021 16:09:33 +0400 Subject: [PATCH] lineage-sdk: Add LineageGlobalActions service The service will offer updateUserConfig, getUserConfig, and userConfigContains methods, which will be used from LineageParts and fwb. The service will also observe Settings.Global.BUGREPORT_IN_POWER_MENU and Settings.Secure.LOCKDOWN_IN_POWER_MENU to update the GlobalActions dialog when these are updated. Signed-off-by: Gegham Zakaryan Change-Id: I46663ab1ffe6f8d8b2a3dc7dbe54a65b7a62046a --- .../internal/LineageGlobalActionsService.java | 185 ++++++++++++++++++ lineage/res/res/values/config.xml | 1 + .../lineageos/app/ILineageGlobalActions.aidl | 29 +++ .../app/LineageContextConstants.java | 18 +- .../lineageos/app/LineageGlobalActions.java | 138 +++++++++++++ 5 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 lineage/lib/main/java/org/lineageos/platform/internal/LineageGlobalActionsService.java create mode 100644 sdk/src/java/lineageos/app/ILineageGlobalActions.aidl create mode 100644 sdk/src/java/lineageos/app/LineageGlobalActions.java diff --git a/lineage/lib/main/java/org/lineageos/platform/internal/LineageGlobalActionsService.java b/lineage/lib/main/java/org/lineageos/platform/internal/LineageGlobalActionsService.java new file mode 100644 index 00000000..2677ae0d --- /dev/null +++ b/lineage/lib/main/java/org/lineageos/platform/internal/LineageGlobalActionsService.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2021 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.platform.internal; + +import static lineageos.providers.LineageSettings.Secure.POWER_MENU_ACTIONS; +import static lineageos.providers.LineageSettings.Secure.getStringForUser; +import static lineageos.providers.LineageSettings.Secure.putStringForUser; + +import static org.lineageos.internal.util.PowerMenuConstants.GLOBAL_ACTION_KEY_BUGREPORT; +import static org.lineageos.internal.util.PowerMenuConstants.GLOBAL_ACTION_KEY_LOCKDOWN; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.UserHandle; +import android.provider.Settings; + +import lineageos.app.LineageContextConstants; +import lineageos.app.ILineageGlobalActions; + +import org.lineageos.internal.util.PowerMenuConstants; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @hide + */ +public class LineageGlobalActionsService extends LineageSystemService { + + private static final String TAG = "LineageGlobalActions"; + + private final Context mContext; + private final ContentResolver mContentResolver; + + private final List mLocalUserConfig = new ArrayList(); + + // Observes user-controlled settings + private GlobalActionsSettingsObserver mObserver; + + public LineageGlobalActionsService(Context context) { + super(context); + + mContext = context; + mContentResolver = mContext.getContentResolver(); + } + + private class GlobalActionsSettingsObserver extends ContentObserver { + + private final Uri BUGREPORT_URI = + Settings.Global.getUriFor(Settings.Global.BUGREPORT_IN_POWER_MENU); + private final Uri LOCKDOWN_URI = + Settings.Secure.getUriFor(Settings.Secure.LOCKDOWN_IN_POWER_MENU); + + public GlobalActionsSettingsObserver(Context context, Handler handler) { + super(handler); + } + + public void observe(boolean enabled) { + if (enabled) { + mContentResolver.registerContentObserver(BUGREPORT_URI, false, this); + mContentResolver.registerContentObserver(LOCKDOWN_URI, false, this); + } else { + mContentResolver.unregisterContentObserver(this); + } + } + + @Override + public void onChange(boolean selfChange) { + updateUserConfigInternal(Settings.Global.getInt(mContentResolver, + Settings.Global.BUGREPORT_IN_POWER_MENU, 0) == 1, + GLOBAL_ACTION_KEY_BUGREPORT); + + updateUserConfigInternal(Settings.Secure.getIntForUser(mContentResolver, + Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, UserHandle.USER_CURRENT) == 1, + GLOBAL_ACTION_KEY_LOCKDOWN); + } + }; + + private void populateUserConfig() { + mLocalUserConfig.clear(); + mLocalUserConfig.addAll(Arrays.asList(getUserConfig())); + } + + private String[] getUserConfig() { + String savedActions = getStringForUser(mContentResolver, + POWER_MENU_ACTIONS, UserHandle.USER_CURRENT); + + if (savedActions == null) { + return mContext.getResources().getStringArray( + com.android.internal.R.array.config_globalActionsList); + } else { + return savedActions.split("\\|"); + } + } + + private void updateUserConfigInternal(boolean enabled, String action) { + if (enabled) { + if (!mLocalUserConfig.contains(action)) { + mLocalUserConfig.add(action); + } + } else { + if (mLocalUserConfig.contains(action)) { + mLocalUserConfig.remove(action); + } + } + saveUserConfig(); + } + + private void saveUserConfig() { + List actions = new ArrayList(); + for (String action : PowerMenuConstants.getAllActions()) { + if (mLocalUserConfig.contains(action)) { + actions.add(action); + } + } + + String s = String.join("|", actions); + putStringForUser(mContentResolver, POWER_MENU_ACTIONS, s, UserHandle.USER_CURRENT); + } + + @Override + public String getFeatureDeclaration() { + return LineageContextConstants.Features.GLOBAL_ACTIONS; + } + + @Override + public void onStart() { + publishBinderService(LineageContextConstants.LINEAGE_GLOBAL_ACTIONS_SERVICE, mBinder); + } + + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_BOOT_COMPLETED) { + populateUserConfig(); + + mObserver = new GlobalActionsSettingsObserver(mContext, null); + mObserver.observe(true); + } + } + + private final IBinder mBinder = new ILineageGlobalActions.Stub() { + + @Override + public void updateUserConfig(boolean enabled, String action) { + updateUserConfigInternal(enabled, action); + } + + @Override + public List getLocalUserConfig() { + populateUserConfig(); + return mLocalUserConfig; + } + + @Override + public String[] getUserActionsArray() { + return getUserConfig(); + } + + @Override + public boolean userConfigContains(String preference) { + return getLocalUserConfig().contains(preference); + } + }; +} diff --git a/lineage/res/res/values/config.xml b/lineage/res/res/values/config.xml index 659a5ac6..abe25949 100644 --- a/lineage/res/res/values/config.xml +++ b/lineage/res/res/values/config.xml @@ -108,6 +108,7 @@ org.lineageos.platform.internal.LineageAudioService org.lineageos.platform.internal.TrustInterfaceService org.lineageos.platform.internal.LineageSettingsService + org.lineageos.platform.internal.LineageGlobalActionsService diff --git a/sdk/src/java/lineageos/app/ILineageGlobalActions.aidl b/sdk/src/java/lineageos/app/ILineageGlobalActions.aidl new file mode 100644 index 00000000..ed8cf3cd --- /dev/null +++ b/sdk/src/java/lineageos/app/ILineageGlobalActions.aidl @@ -0,0 +1,29 @@ +/* + * Copyright 2021 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 lineageos.app; + +/** @hide */ +interface ILineageGlobalActions { + + void updateUserConfig(boolean enabled, String action); + + List getLocalUserConfig(); + + String[] getUserActionsArray(); + + boolean userConfigContains(String preference); +} diff --git a/sdk/src/java/lineageos/app/LineageContextConstants.java b/sdk/src/java/lineageos/app/LineageContextConstants.java index 0b435b4d..cd5ffd30 100644 --- a/sdk/src/java/lineageos/app/LineageContextConstants.java +++ b/sdk/src/java/lineageos/app/LineageContextConstants.java @@ -1,5 +1,6 @@ /** - * Copyright (c) 2015, The CyanogenMod Project + * Copyright (C) 2015, The CyanogenMod Project + * Copyright (C) 2017-2021 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. @@ -107,6 +108,13 @@ public final class LineageContextConstants { */ public static final String LINEAGE_TRUST_INTERFACE = "lineagetrust"; + /** + * Update power menu (GlobalActions) + * + * @hide + */ + public static final String LINEAGE_GLOBAL_ACTIONS_SERVICE = "lineageglobalactions"; + /** * Features supported by the Lineage SDK. */ @@ -182,5 +190,13 @@ public final class LineageContextConstants { */ @SdkConstant(SdkConstant.SdkConstantType.FEATURE) public static final String FOD = "vendor.lineage.biometrics.fingerprint.inscreen"; + + /** + * Feature for {@link PackageManager#getSystemAvailableFeatures} and + * {@link PackageManager#hasSystemFeature}: The device includes the lineage globalactions + * service utilized by the lineage sdk and LineageParts. + */ + @SdkConstant(SdkConstant.SdkConstantType.FEATURE) + public static final String GLOBAL_ACTIONS = "org.lineageos.globalactions"; } } diff --git a/sdk/src/java/lineageos/app/LineageGlobalActions.java b/sdk/src/java/lineageos/app/LineageGlobalActions.java new file mode 100644 index 00000000..e475e124 --- /dev/null +++ b/sdk/src/java/lineageos/app/LineageGlobalActions.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 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 lineageos.app; + +import java.util.List; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +public class LineageGlobalActions { + + private Context mContext; + + private static ILineageGlobalActions sService; + private static LineageGlobalActions sLineageGlobalActionsInstance; + + private static final String TAG = "LineageGlobalActions"; + + private LineageGlobalActions(Context context) { + Context appContext = context.getApplicationContext(); + if (appContext != null) { + mContext = appContext; + } else { + mContext = context; + } + + try { + sService = getService(); + } catch (RemoteException e) { + sService = null; + } + + if (sService == null) { + Log.wtf(TAG, "Unable to get LineageGlobalActionsService. The service either" + + " crashed, was not started, or the interface has been called to early in" + + " SystemServer init"); + } + } + + /** + * Get or create an instance of the {@link lineageos.app.LineageGlobalActions} + * @param context + * @return {@link LineageGlobalActions} + */ + public static LineageGlobalActions getInstance(Context context) { + if (sLineageGlobalActionsInstance == null) { + sLineageGlobalActionsInstance = new LineageGlobalActions(context); + } + return sLineageGlobalActionsInstance; + } + + /** @hide */ + static public ILineageGlobalActions getService() throws RemoteException { + if (sService != null) { + return sService; + } + IBinder b = ServiceManager.getService( + LineageContextConstants.LINEAGE_GLOBAL_ACTIONS_SERVICE); + sService = ILineageGlobalActions.Stub.asInterface(b); + + if (sService == null) { + throw new RemoteException("Couldn't get " + + LineageContextConstants.LINEAGE_GLOBAL_ACTIONS_SERVICE + + " on binder"); + } + return sService; + } + + + /** + * Update the action to the state. + * @param enabled a {@link Boolean} value + * @param action a {@link String} value + */ + public void updateUserConfig(boolean enabled, String action) { + try { + getService().updateUserConfig(enabled, action); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the user configuration as {@link List}. + * @return {@link List} + */ + public List getLocalUserConfig() { + try { + return getService().getLocalUserConfig(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the user configuration as {@link String[]} in the same order as in the power menu. + * Actions are separated with | delimiter. + * @return {@link String[]} + */ + public String[] getUserActionsArray() { + try { + return getService().getUserActionsArray(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Check if user configuration ({@link List}) contains + * preference ({@link String}) + * @param preference {@link String} + * @return {@link boolean} + */ + public boolean userConfigContains(String preference) { + try { + return getService().userConfigContains(preference); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +}