diff --git a/lineage/lib/main/java/org/lineageos/platform/internal/BlockerInterfaceService.java b/lineage/lib/main/java/org/lineageos/platform/internal/BlockerInterfaceService.java new file mode 100644 index 0000000000000000000000000000000000000000..056221bad8670026c788c98d7814e9857c5ffdf2 --- /dev/null +++ b/lineage/lib/main/java/org/lineageos/platform/internal/BlockerInterfaceService.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2018-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.platform.internal; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.lang.InterruptedException; + +import lineageos.app.LineageContextConstants; +import lineageos.providers.LineageSettings; +import lineageos.blockers.IBlockerInterface; +import lineageos.blockers.BlockerInterface; + +import java.util.NoSuchElementException; + +/** @hide **/ +public class BlockerInterfaceService extends LineageSystemService { + private static final String TAG = "LineageBlockerInterfaceService"; + + private Context mContext; + + public BlockerInterfaceService(Context context) { + super(context); + mContext = context; + if (context.getPackageManager().hasSystemFeature(LineageContextConstants.Features.BLOCKER)) { + publishBinderService(LineageContextConstants.LINEAGE_BLOCKER_INTERFACE, mService); + } else { + Log.wtf(TAG, "Blocker service started by system server but feature xml not" + + " declared. Not publishing binder service!"); + } + } + + @Override + public String getFeatureDeclaration() { + return LineageContextConstants.Features.BLOCKER; + } + + @Override + public void onStart() { + runTestInternal(true); + } + + + private void runTestInternal(boolean fromStart) { + // TODO: Dummy method. replace with appropriate methods. + Log.d(TAG, "RunTestInternal called with "+fromStart); + Runtime r = Runtime.getRuntime(); + try { + Process p = r.exec("/sbin/su -c iptables -L -w"); + String result=""; + String temp= null; + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + while((temp = reader.readLine()) != null) { + Log.d(TAG, "RunTestInternal tempResult: "+temp); + result.concat(temp); + } + Log.d(TAG, "RunTestInternal result: "+result); + + BufferedReader errReader = new BufferedReader(new InputStreamReader(p.getErrorStream())); + while((temp = errReader.readLine()) != null) { + Log.d(TAG, "RunTestInternal error: "+temp); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private boolean blockIpOrDomainInternal(String ipOrDomain) { + Log.d(TAG, "blockIpOrDomainInternal called with " + ipOrDomain); + String iptableCmd = "iptables -A INPUT -m string --hex-string \"" + ipOrDomain + "\" --algo bm -j DROP && iptables -A OUTPUT -m string --hex-string \"" + ipOrDomain + "\" --algo bm -j DROP"; + String[] cmds = new String[] {"su", "-c", iptableCmd}; + Runtime r = Runtime.getRuntime(); + try { + Process p = r.exec(cmds); + + String result=""; + String temp= null; + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + while((temp = reader.readLine()) != null) { + Log.d(TAG, "blockIpOrDomainInternal tempResult: "+temp); + result.concat(temp); + } + Log.d(TAG, "blockIpOrDomainInternal result: "+result); + + BufferedReader errReader = new BufferedReader(new InputStreamReader(p.getErrorStream())); + while((temp = errReader.readLine()) != null) { + Log.d(TAG, "blockIpOrDomainInternal error: "+temp); + } + p.waitFor(); + + if (p.exitValue() == 0) return true; + else return false; + } catch (InterruptedException | IOException e) { + e.printStackTrace(); + return false; + } + } + + private boolean unblockIpOrDomainInternal(String ipOrDomain) { + Log.d(TAG, "unblockIpOrDomainInternal called with " + ipOrDomain); + String iptableCmd = "iptables -D INPUT -m string --hex-string \"" + ipOrDomain + "\" --algo bm -j DROP && iptables -D OUTPUT -m string --hex-string \"" + ipOrDomain + "\" --algo bm -j DROP"; + String[] cmds = new String[] {"su", "-c", iptableCmd}; + Runtime r = Runtime.getRuntime(); + try { + Process p = r.exec(cmds); + + String result=""; + String temp= null; + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + while((temp = reader.readLine()) != null) { + Log.d(TAG, "unblockIpOrDomainInternal tempResult: "+temp); + result.concat(temp); + } + Log.d(TAG, "unblockIpOrDomainInternal result: "+result); + + BufferedReader errReader = new BufferedReader(new InputStreamReader(p.getErrorStream())); + while((temp = errReader.readLine()) != null) { + Log.d(TAG, "unblockIpOrDomainInternal error: "+temp); + } + p.waitFor(); + + if (p.exitValue() == 0) return true; + else return false; + } catch (InterruptedException | IOException e) { + e.printStackTrace(); + return false; + } + } + + + /* Utils */ + + private void enforceBlockerPermission() { + mContext.enforceCallingOrSelfPermission(BlockerInterface.BLOCKER_INTERFACE_PERMISSION, + "You do not have permissions to use the Blocker interface"); + } + + /* Service */ + + private final IBinder mService = new IBlockerInterface.Stub() { + @Override + public void runTest() { + enforceBlockerPermission(); + /* + * We need to clear the caller's identity in order to + * allow this method call to modify settings + * not allowed by the caller's permissions. + */ + long token = clearCallingIdentity(); + runTestInternal(false); + restoreCallingIdentity(token); + } + + @Override + public boolean blockIp(String ipAddress) { + enforceBlockerPermission(); + /* + * We need to clear the caller's identity in order to + * allow this method call to modify settings + * not allowed by the caller's permissions. + */ + long token = clearCallingIdentity(); + boolean result = blockIpOrDomainInternal(ipAddress); + restoreCallingIdentity(token); + return result; + } + + @Override + public boolean unblockIp(String ipAddress) { + enforceBlockerPermission(); + /* + * We need to clear the caller's identity in order to + * allow this method call to modify settings + * not allowed by the caller's permissions. + */ + long token = clearCallingIdentity(); + boolean result = unblockIpOrDomainInternal(ipAddress); + restoreCallingIdentity(token); + return result; + } + + @Override + public boolean blockDomain(String domainName) { + enforceBlockerPermission(); + /* + * We need to clear the caller's identity in order to + * allow this method call to modify settings + * not allowed by the caller's permissions. + */ + long token = clearCallingIdentity(); + boolean result = blockIpOrDomainInternal(domainName); + restoreCallingIdentity(token); + return result; + } + + @Override + public boolean unblockDomain(String domainName) { + enforceBlockerPermission(); + /* + * We need to clear the caller's identity in order to + * allow this method call to modify settings + * not allowed by the caller's permissions. + */ + long token = clearCallingIdentity(); + boolean result = unblockIpOrDomainInternal(domainName); + restoreCallingIdentity(token); + return result; + } + }; +} diff --git a/lineage/res/AndroidManifest.xml b/lineage/res/AndroidManifest.xml index e6ec86d25a100e3964cd052f58fc52b1da8e7273..3ec2209059dc3e4aba1d97245f5a717ae035812c 100644 --- a/lineage/res/AndroidManifest.xml +++ b/lineage/res/AndroidManifest.xml @@ -125,6 +125,13 @@ android:icon="@drawable/ic_trust" android:protectionLevel="signature|privileged" /> + + + org.lineageos.platform.internal.LineageAudioService org.lineageos.platform.internal.TrustInterfaceService org.lineageos.platform.internal.LineageSettingsService + org.lineageos.platform.internal.BlockerInterfaceService diff --git a/lineage/res/res/values/strings.xml b/lineage/res/res/values/strings.xml index ab06347fd92c3918d15eca49a9328ed749eaa25e..4ff33cca807b62ac99f8c7f79bcb1630c77df050 100644 --- a/lineage/res/res/values/strings.xml +++ b/lineage/res/res/values/strings.xml @@ -171,4 +171,8 @@ Discover Trust Get to know how to assure your device is safe Manage alerts + + + Access Blocker interface + Allows an app to access Blocker diff --git a/sdk/src/java/lineageos/app/LineageContextConstants.java b/sdk/src/java/lineageos/app/LineageContextConstants.java index 0b435b4da117829917d2855278a8ae601c1b15d7..b0eceaf3399eec7abbfaa85779e0676ed2e785a4 100644 --- a/sdk/src/java/lineageos/app/LineageContextConstants.java +++ b/sdk/src/java/lineageos/app/LineageContextConstants.java @@ -107,6 +107,16 @@ public final class LineageContextConstants { */ public static final String LINEAGE_TRUST_INTERFACE = "lineagetrust"; + /** + * Use with {@link android.content.Context#getSystemService} to retrieve a + * {@link lineageos.blockers.BlockerInterface} to access the Blocker interface. + * + * @see android.content.Context#getSystemService + * @see lineageos.blockers.BlockerInterface + * + * @hide + */ + public static final String LINEAGE_BLOCKER_INTERFACE = "lineageblocker"; /** * Features supported by the Lineage SDK. */ @@ -182,5 +192,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 /e/ blocker service + * utilized by the lineage sdk. + */ + @SdkConstant(SdkConstant.SdkConstantType.FEATURE) + public static final String BLOCKER = "org.lineageos.blocker"; } } diff --git a/sdk/src/java/lineageos/blockers/BlockerInterface.java b/sdk/src/java/lineageos/blockers/BlockerInterface.java new file mode 100644 index 0000000000000000000000000000000000000000..500b55819935df756dc75651ef28fbcced3efea8 --- /dev/null +++ b/sdk/src/java/lineageos/blockers/BlockerInterface.java @@ -0,0 +1,149 @@ +/** + * Copyright (C) 2018-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 lineageos.blockers; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import lineageos.app.LineageContextConstants; + +public class BlockerInterface { + /** + * Allows an application to use the Blocker interface to provide blocking trackers/ads + * feature to the user. + * This is a system-only permission, user-installed apps cannot use it + */ + public static final String BLOCKER_INTERFACE_PERMISSION = "lineageos.permission.ACCESS_BLOCKER"; + + private static final String TAG = "BlockerInterface"; + + private static IBlockerInterface sService; + private static BlockerInterface sInstance; + + private Context mContext; + + private BlockerInterface(Context context) { + Context appContext = context.getApplicationContext(); + mContext = appContext == null ? context : appContext; + sService = getService(); + if (context.getPackageManager().hasSystemFeature( + LineageContextConstants.Features.BLOCKER) && sService == null) { + throw new RuntimeException("Unable to get BlockerInterfaceService. 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.blockers.BlockerInterface} + * + * @param context Used to get the service + * @return {@link BlockerInterface} + */ + public static BlockerInterface getInstance(Context context) { + if (sInstance == null) { + sInstance = new BlockerInterface(context); + } + return sInstance; + } + + /** @hide **/ + public static IBlockerInterface getService() { + if (sService != null) { + return sService; + } + IBinder b = ServiceManager.getService(LineageContextConstants.LINEAGE_BLOCKER_INTERFACE); + sService = IBlockerInterface.Stub.asInterface(b); + + if (b == null) { + Log.e(TAG, "null service. SAD!"); + return null; + } + + sService = IBlockerInterface.Stub.asInterface(b); + return sService; + } + + public void runTest() { + if (sService == null) { + return; + } + try { + Log.d(TAG, "Service method called."); + sService.runTest(); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + } + return; + } + + public boolean blockIp(String ipAddress) { + if (sService == null) { + return false; + } + try { + Log.d(TAG, "BlockIP method called."); + return sService.blockIp(ipAddress); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + return false; + } + } + + public boolean unblockIp(String ipAddress) { + if (sService == null) { + return false; + } + try { + Log.d(TAG, "BlockIP method called."); + return sService.unblockIp(ipAddress); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + return false; + } + } + + public boolean blockDomain(String domainName) { + if (sService == null) { + return false; + } + try { + Log.d(TAG, "BlockIP method called."); + return sService.blockDomain(domainName); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + return false; + } + } + + public boolean unblockDomain(String domainName) { + if (sService == null) { + return false; + } + try { + Log.d(TAG, "BlockIP method called."); + return sService.unblockDomain(domainName); + } catch (RemoteException e) { + Log.e(TAG, e.getLocalizedMessage(), e); + return false; + } + } + +} diff --git a/sdk/src/java/lineageos/blockers/IBlockerInterface.aidl b/sdk/src/java/lineageos/blockers/IBlockerInterface.aidl new file mode 100644 index 0000000000000000000000000000000000000000..d7953a0ccfe5aead033b3e3f445ba2a0a86029ea --- /dev/null +++ b/sdk/src/java/lineageos/blockers/IBlockerInterface.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package lineageos.blockers; + +/** {@hide} */ +interface IBlockerInterface { + + void runTest(); + + boolean blockIp(String ipAddress); + + boolean unblockIp(String ipAddress); + + boolean blockDomain(String domainName); + + boolean unblockDomain(String domainName); +}