-
Notifications
You must be signed in to change notification settings - Fork 282
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[AppInfo] Add option to enable/disable sensors
Location: App Details page > App Info tab > 3-dots menu > Sensors Signed-off-by: Muntashir Al-Islam <muntashirakon@riseup.net>
- Loading branch information
1 parent
544d927
commit ab73dfc
Showing
8 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
app/src/main/java/io/github/muntashirakon/AppManager/compat/SensorServiceCompat.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
|
||
package io.github.muntashirakon.AppManager.compat; | ||
|
||
import android.annotation.UserIdInt; | ||
import android.os.Build; | ||
import android.os.IBinder; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.RequiresApi; | ||
import androidx.annotation.RequiresPermission; | ||
|
||
import java.io.IOException; | ||
|
||
import io.github.muntashirakon.AppManager.ipc.ProxyBinder; | ||
import io.github.muntashirakon.AppManager.utils.BinderShellExecutor; | ||
|
||
@RequiresApi(Build.VERSION_CODES.P) | ||
public final class SensorServiceCompat { | ||
@RequiresPermission(ManifestCompat.permission.MANAGE_SENSORS) | ||
public static boolean isSensorEnabled(@NonNull String packageName, @UserIdInt int userId) { | ||
String[] command; | ||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { | ||
command = new String[]{"get-uid-state", packageName, "--user", String.valueOf(userId)}; | ||
} else command = new String[]{"get-uid-state", packageName}; | ||
try { | ||
BinderShellExecutor.ShellResult result = BinderShellExecutor.execute(getSensorService(), command); | ||
return result.getResultCode() == 0 && "active".contains(result.getStdout()); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
return true; | ||
} | ||
|
||
@RequiresPermission(ManifestCompat.permission.MANAGE_SENSORS) | ||
public static void enableSensor(@NonNull String packageName, @UserIdInt int userId, boolean enable) throws IOException { | ||
String state = enable ? "active" : "idle"; | ||
String[] command; | ||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { | ||
command = new String[]{"set-uid-state", packageName, state, "--user", String.valueOf(userId)}; | ||
} else command = new String[]{"set-uid-state", packageName, state}; | ||
BinderShellExecutor.ShellResult result = BinderShellExecutor.execute(getSensorService(), command); | ||
if (result.getResultCode() != 0) { | ||
throw new IOException("Could not " + (enable ? "enable" : "disable") + " sensor."); | ||
} | ||
} | ||
|
||
@RequiresPermission(ManifestCompat.permission.MANAGE_SENSORS) | ||
public static void resetSensor(@NonNull String packageName, @UserIdInt int userId) throws IOException { | ||
String[] command; | ||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { | ||
command = new String[]{"reset-uid-state", packageName, "--user", String.valueOf(userId)}; | ||
} else command = new String[]{"reset-uid-state", packageName}; | ||
BinderShellExecutor.ShellResult result = BinderShellExecutor.execute(getSensorService(), command); | ||
if (result.getResultCode() != 0) { | ||
throw new IOException("Could not reset sensor."); | ||
} | ||
} | ||
|
||
@NonNull | ||
private static IBinder getSensorService() { | ||
return ProxyBinder.getService("sensorservice"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 137 additions & 0 deletions
137
app/src/main/java/io/github/muntashirakon/AppManager/utils/BinderShellExecutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
|
||
package io.github.muntashirakon.AppManager.utils; | ||
|
||
import android.os.Build; | ||
import android.os.Bundle; | ||
import android.os.IBinder; | ||
import android.os.ParcelFileDescriptor; | ||
import android.os.RemoteException; | ||
import android.os.ResultReceiver; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
import androidx.annotation.RequiresApi; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.File; | ||
import java.io.FileDescriptor; | ||
import java.io.FileInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.concurrent.CountDownLatch; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import io.github.muntashirakon.AppManager.ipc.ProxyBinder; | ||
import io.github.muntashirakon.io.Path; | ||
|
||
@RequiresApi(Build.VERSION_CODES.N) | ||
public class BinderShellExecutor { | ||
public static class ShellResult { | ||
private int resultCode; | ||
private String stdout; | ||
private String stderr; | ||
|
||
private ShellResult() { | ||
} | ||
|
||
public int getResultCode() { | ||
return resultCode; | ||
} | ||
|
||
public String getStdout() { | ||
return stdout; | ||
} | ||
|
||
public String getStderr() { | ||
return stderr; | ||
} | ||
} | ||
|
||
public static ShellResult execute(@NonNull IBinder binder, @NonNull String[] command, | ||
@Nullable File input) throws IOException { | ||
if (input == null) { | ||
return execute(binder, command); | ||
} | ||
try (FileInputStream out = new FileInputStream(input)) { | ||
return execute(binder, command, out); | ||
} | ||
} | ||
|
||
public static ShellResult execute(@NonNull IBinder binder, @NonNull String[] command, | ||
@Nullable Path input) throws IOException { | ||
if (input == null) { | ||
return execute(binder, command); | ||
} | ||
try (InputStream os = input.openInputStream()) { | ||
return execute(binder, command, os); | ||
} | ||
} | ||
|
||
public static ShellResult execute(@NonNull IBinder binder, @NonNull String[] command, | ||
@Nullable FileInputStream input) throws IOException { | ||
if (input == null) { | ||
return execute(binder, command); | ||
} | ||
return execute(binder, command, input.getFD()); | ||
} | ||
|
||
public static ShellResult execute(@NonNull IBinder binder, @NonNull String[] command, @Nullable InputStream input) throws IOException { | ||
if (input == null) { | ||
return execute(binder, command); | ||
} | ||
ParcelFileDescriptor inputFd = ParcelFileDescriptorUtil.pipeFrom(input); | ||
return execute(binder, command, inputFd.getFileDescriptor()); | ||
} | ||
|
||
public static ShellResult execute(@NonNull IBinder binder, @NonNull String[] command, | ||
@Nullable FileDescriptor input) throws IOException { | ||
if (input == null) { | ||
return execute(binder, command); | ||
} | ||
return executeInternal(binder, command, input); | ||
} | ||
|
||
public static ShellResult execute(@NonNull IBinder binder, @NonNull String[] command) | ||
throws IOException { | ||
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); | ||
ParcelFileDescriptor readSide = pipe[0]; | ||
ParcelFileDescriptor writeSide = pipe[1]; | ||
return executeInternal(binder, command, readSide.getFileDescriptor()); | ||
} | ||
|
||
private static ShellResult executeInternal(@NonNull IBinder binder, @NonNull String[] command, | ||
@NonNull FileDescriptor in) throws IOException { | ||
try (ByteArrayOutputStream stdoutStream = new ByteArrayOutputStream(); | ||
ByteArrayOutputStream stderrStream = new ByteArrayOutputStream()) { | ||
ParcelFileDescriptor stdoutFd = ParcelFileDescriptorUtil.pipeTo(stdoutStream); | ||
ParcelFileDescriptor stderrFd = ParcelFileDescriptorUtil.pipeTo(stderrStream); | ||
AtomicInteger atomicResultCode = new AtomicInteger(-1); | ||
CountDownLatch sem = new CountDownLatch(1); | ||
ProxyBinder.shellCommand(binder, in, stdoutFd.getFileDescriptor(), stderrFd.getFileDescriptor(), command, null, new ResultReceiver(ThreadUtils.getUiThreadHandler()) { | ||
@Override | ||
protected void onReceiveResult(int resultCode, Bundle resultData) { | ||
atomicResultCode.set(resultCode); | ||
sem.countDown(); | ||
} | ||
}); | ||
try { | ||
sem.await(); | ||
} catch (InterruptedException ignore) { | ||
} | ||
int resultCode = atomicResultCode.get(); | ||
if (resultCode == -1) { | ||
throw new IOException("Invalid result code " + resultCode); | ||
} | ||
ShellResult result = new ShellResult(); | ||
result.resultCode = resultCode; | ||
result.stdout = stdoutStream.toString(); | ||
result.stderr = stderrStream.toString(); | ||
return result; | ||
} catch (RemoteException e) { | ||
return ExUtils.rethrowFromSystemServer(e); | ||
} catch (Throwable th) { | ||
return ExUtils.rethrowAsIOException(th); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters