Skip to content

Commit

Permalink
[Refactor] Handle the possibility of remote server running under any UID
Browse files Browse the repository at this point in the history
Signed-off-by: Muntashir Al-Islam <[email protected]>
  • Loading branch information
MuntashirAkon committed Apr 21, 2024
1 parent c47ce6f commit 04b8756
Show file tree
Hide file tree
Showing 13 changed files with 67 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ private void updateProperties(@NonNull FileProperties fileProperties) {
mOwnerLayout.setEndIconVisible(isPhysicalWritable);
mGroupLayout.setEndIconVisible(isPhysicalWritable);
mModeLayout.setEndIconVisible(isPhysicalWritable);
mSelinuxContextLayout.setEndIconVisible(Ops.isRoot() && isPhysicalWritable);
mSelinuxContextLayout.setEndIconVisible(Ops.isWorkingUidRoot() && isPhysicalWritable);
}
if (noInit || mFileProperties.mode != fileProperties.mode) {
mModeView.setText(fileProperties.mode != 0 ? FmUtils.getFormattedMode(fileProperties.mode) : "--");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ public void onAuthenticated(Bundle savedInstanceState) {
findViewById(R.id.progress_linear).setVisibility(View.GONE);
// Get Intent
Intent intent = new Intent(getIntent());
mUseRoot = Ops.isRoot() && intent.getBooleanExtra(EXTRA_ROOT, false);
mUseRoot = Ops.isWorkingUidRoot() && intent.getBooleanExtra(EXTRA_ROOT, false);
mUserHandle = intent.getIntExtra(EXTRA_USER_HANDLE, UserHandleHidden.myUserId());
intent.removeExtra(EXTRA_ROOT);
intent.removeExtra(EXTRA_USER_HANDLE);
Expand Down Expand Up @@ -572,7 +572,7 @@ private void setupVariables() {
// Setup root
MaterialCheckBox useRootCheckBox = findViewById(R.id.use_root);
useRootCheckBox.setChecked(mUseRoot);
useRootCheckBox.setVisibility(Ops.isRoot() ? View.VISIBLE : View.GONE);
useRootCheckBox.setVisibility(Ops.isWorkingUidRoot() ? View.VISIBLE : View.GONE);
useRootCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (mUseRoot != isChecked) {
mUseRoot = isChecked;
Expand Down Expand Up @@ -815,7 +815,7 @@ private void pasteIntentDetails() {
StringTokenizer tokenizer = new StringTokenizer(line, "\t");
switch (tokenizer.nextToken()) {
case "ROOT":
mUseRoot = Ops.isRoot() && Boolean.parseBoolean(tokenizer.nextToken());
mUseRoot = Ops.isWorkingUidRoot() && Boolean.parseBoolean(tokenizer.nextToken());
++parseCount;
break;
case "USER":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package io.github.muntashirakon.AppManager.ipc;

import android.os.Process;
import android.os.RemoteException;

import androidx.annotation.AnyThread;
Expand All @@ -15,6 +16,7 @@
import io.github.muntashirakon.AppManager.BuildConfig;
import io.github.muntashirakon.AppManager.IAMService;
import io.github.muntashirakon.AppManager.misc.NoOps;
import io.github.muntashirakon.AppManager.settings.Ops;
import io.github.muntashirakon.AppManager.utils.ThreadUtils;
import io.github.muntashirakon.io.FileSystemManager;

Expand All @@ -40,6 +42,8 @@ public static void bindServices() throws RemoteException {
throw new RemoteException("IAmService not running.");
}
getFileSystemManager();
// Update UID
Ops.setWorkingUid(getAmService().getUid());
}

public static boolean alive() {
Expand All @@ -50,7 +54,7 @@ public static boolean alive() {

@WorkerThread
@NoOps(used = true)
public static void bindFileSystemManager() throws RemoteException {
private static void bindFileSystemManager() throws RemoteException {
synchronized (sFileSystemServiceConnectionWrapper) {
try {
sFileSystemServiceConnectionWrapper.bindService();
Expand Down Expand Up @@ -111,6 +115,7 @@ public static void stopServices() {
synchronized (sFileSystemServiceConnectionWrapper) {
sFileSystemServiceConnectionWrapper.stopDaemon();
}
Ops.setWorkingUid(Process.myUid());
}

@MainThread
Expand All @@ -121,6 +126,7 @@ public static void unbindServices() {
synchronized (sFileSystemServiceConnectionWrapper) {
sFileSystemServiceConnectionWrapper.unbindService();
}
Ops.setWorkingUid(Process.myUid());
}

@WorkerThread
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,11 @@ private static Runnable asRunnable(Shell.Task task) {
task.run(os, null, null);
// The whole command has now been fetched.
String cmd = os.toString();
if (Ops.isRoot()) {
if (Ops.isDirectRoot()) {
if (!Runner.runCommand(cmd).isSuccessful()) {
Log.e(TAG, "Couldn't start service using root.", new Throwable());
}
} else if (LocalServer.alive(ContextUtils.getContext())) {
// ADB must be checked at first
if (LocalServer.getInstance().runCommand(cmd).getStatusCode() != 0) {
Log.e(TAG, "Couldn't start service using ADB.", new Throwable());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@

package io.github.muntashirakon.AppManager.magisk;

import static io.github.muntashirakon.AppManager.magisk.MagiskUtils.ISOLATED_MAGIC;

import android.content.pm.PackageInfo;

import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;

import java.util.Collection;
import java.util.List;

import io.github.muntashirakon.AppManager.runner.Runner;
import io.github.muntashirakon.AppManager.settings.Ops;

import static io.github.muntashirakon.AppManager.magisk.MagiskUtils.ISOLATED_MAGIC;

@AnyThread
@WorkerThread
public class MagiskDenyList {
/**
* Whether Magisk DenyList is available.
*/
public static boolean available() {
return Ops.isRoot() && Runner.runCommand(new String[]{"magisk", "--denylist", "ls"}).isSuccessful();
return Ops.isWorkingUidRoot() && Runner.runCommand(new String[]{"magisk", "--denylist", "ls"}).isSuccessful();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@

package io.github.muntashirakon.AppManager.magisk;

import static io.github.muntashirakon.AppManager.magisk.MagiskUtils.ISOLATED_MAGIC;

import android.content.pm.PackageInfo;

import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;

import java.util.Collection;
import java.util.List;

import io.github.muntashirakon.AppManager.runner.Runner;
import io.github.muntashirakon.AppManager.settings.Ops;

import static io.github.muntashirakon.AppManager.magisk.MagiskUtils.ISOLATED_MAGIC;

@AnyThread
@WorkerThread
public class MagiskHide {
/**
* Whether MagiskHide is available.
*/
public static boolean available() {
return Ops.isRoot() && Runner.runCommand(new String[]{"command", "-v", "magiskhide"}).isSuccessful();
return Ops.isWorkingUidRoot() && Runner.runCommand(new String[]{"command", "-v", "magiskhide"}).isSuccessful();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ public LinkedHashMap<Integer, Integer> getFilterFlagLocaleMap() {
put(FILTER_APPS_WITHOUT_BACKUPS, R.string.filter_apps_without_backups);
put(FILTER_RUNNING_APPS, R.string.filter_running_apps);
put(FILTER_APPS_WITH_SPLITS, R.string.filter_apps_with_splits);
if (Ops.isRoot()) {
if (Ops.isWorkingUidRoot()) {
put(FILTER_APPS_WITH_KEYSTORE, R.string.filter_apps_with_keystore);
put(FILTER_APPS_WITH_SAF, R.string.filter_apps_with_saf);
put(FILTER_APPS_WITH_SSAID, R.string.filter_apps_with_ssaid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public List<String> getStderr() {

@NonNull
private static Runner getInstance() {
if (Ops.isRoot()) {
if (Ops.isDirectRoot()) {
return getRootInstance();
} else if (LocalServices.alive()) {
return getPrivilegedInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ public boolean onSelectionChange(int selectionCount) {
forceStop.setEnabled(appsCount != 0 && appsCount == selectedItems.size());
forceStop.setVisible(SelfPermissions.checkSelfOrRemotePermission(ManifestCompat.permission.FORCE_STOP_PACKAGES));
preventBackground.setEnabled(appsCount != 0 && appsCount == selectedItems.size());
boolean killEnabled = Ops.isRoot();
boolean killEnabled = Ops.isWorkingUidRoot();
if (killEnabled && !mEnableKillForSystem) {
for (ProcessItem item : selectedItems) {
if (item.uid < Process.FIRST_APPLICATION_UID) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ private void onBindViewHolder(@NonNull BodyViewHolder holder, int position) {
Menu menu = popupMenu.getMenu();
// Set kill
MenuItem killItem = menu.findItem(R.id.action_kill);
if ((processItem.uid >= Process.FIRST_APPLICATION_UID || Prefs.RunningApps.enableKillForSystemApps()) && Ops.isRoot()) {
if ((processItem.uid >= Process.FIRST_APPLICATION_UID || Prefs.RunningApps.enableKillForSystemApps()) && Ops.isWorkingUidRoot()) {
killItem.setVisible(true).setOnMenuItemClickListener(item -> {
mModel.killProcess(processItem);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ private ClientSession getSession() throws IOException, AdbPairingRequiredExcepti
try {
mSession = createSession();
} catch (Exception e) {
if (!Ops.isRoot() && !Ops.isAdb()) {
if (!Ops.isDirectRoot() && !Ops.isAdb()) {
// Do not bother attempting to create a new session
throw new IOException("Could not create session", e);
}
Expand Down Expand Up @@ -263,7 +263,7 @@ private void useRootStartServer() throws Exception {
private void startServer() throws Exception {
if (Ops.isAdb()) {
useAdbStartServer();
} else if (Ops.isRoot()) {
} else if (Ops.isDirectRoot()) {
useRootStartServer();
} else throw new Exception("Neither root nor ADB mode is enabled.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;

Expand All @@ -17,6 +18,7 @@
import io.github.muntashirakon.AppManager.logs.Log;
import io.github.muntashirakon.AppManager.server.common.ConfigParams;
import io.github.muntashirakon.AppManager.server.common.ServerActions;
import io.github.muntashirakon.AppManager.settings.Ops;
import io.github.muntashirakon.AppManager.utils.ThreadUtils;
import io.github.muntashirakon.adb.AdbPairingRequiredException;

Expand All @@ -40,18 +42,22 @@ public void onReceive(Context context, @NonNull Intent intent) {
switch (action) {
case ServerActions.ACTION_SERVER_STARTED:
// Server was started for the first time
Ops.setWorkingUid(uid);
startServerIfNotAlready(context);
// TODO: 8/4/24 Need to broadcast this message to update UI and/or trigger development
break;
case ServerActions.ACTION_SERVER_STOPPED:
// Server was stopped
LocalServer.die();
Ops.setWorkingUid(Process.myUid());
break;
case ServerActions.ACTION_SERVER_CONNECTED:
// Server was connected with App Manager
Ops.setWorkingUid(uid);
break;
case ServerActions.ACTION_SERVER_DISCONNECTED:
// Exited from App Manager
Ops.setWorkingUid(Process.myUid());
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ public class Ops {
public static int PHONE_UID = Process.PHONE_UID;
public static int SYSTEM_UID = Process.SYSTEM_UID;

private static volatile int sWorkingUid = Process.myUid();
private static volatile boolean sDirectRoot = false; // AM has root AND that root is being used
private static boolean sIsAdb = false; // UID = 2000
private static boolean sIsSystem = false; // UID = 1000
private static boolean sIsRoot = false; // UID = 0
Expand All @@ -113,23 +115,37 @@ public class Ops {
private Ops() {
}

/**
* Whether App Manager is running in the privileged mode.
*
* @return {@code true} iff user chose to run App Manager in the privileged mode.
*/
@AnyThread
private static boolean isPrivileged() {
// Currently, root and ADB are the only privileged mode
return sIsRoot || sIsAdb || sIsSystem;
public static int getWorkingUid() {
return sWorkingUid;
}

@AnyThread
public static void setWorkingUid(int newUid) {
sWorkingUid = newUid;
}

@AnyThread
public static int getWorkingUidOrRoot() {
int uid = getWorkingUid();
if (uid != ROOT_UID && sDirectRoot) {
return ROOT_UID;
}
return uid;
}

@AnyThread
public static boolean isWorkingUidRoot() {
return getWorkingUid() == ROOT_UID;
}

/**
* Whether App Manager is running in root mode
* Whether App Manager is currently using direct root (e.g. root granted to the app) to perform operations. The
* result returned by this method may not reflect the actual state due to other factors.
*/
@AnyThread
public static boolean isRoot() {
return sIsRoot;
public static boolean isDirectRoot() {
return sDirectRoot;
}

/**
Expand Down Expand Up @@ -219,6 +235,7 @@ public static void setMode(@NonNull String newMode) {
@Status
public static int init(@NonNull Context context, boolean force) {
String mode = getMode();
sDirectRoot = hasRoot();
if (MODE_AUTO.equals(mode)) {
autoDetectRootSystemOrAdbAndPersist(context);
return sIsAdb ? STATUS_SUCCESS : initPermissionsWithSuccess();
Expand All @@ -228,6 +245,7 @@ public static int init(@NonNull Context context, boolean force) {
return sIsAdb ? STATUS_SUCCESS : initPermissionsWithSuccess();
}
if (MODE_NO_ROOT.equals(mode)) {
sDirectRoot = false;
sIsAdb = sIsSystem = sIsRoot = false;
// Also, stop existing services if any
ExUtils.exceptionAsIgnored(() -> {
Expand All @@ -241,7 +259,7 @@ public static int init(@NonNull Context context, boolean force) {
try {
switch (mode) {
case MODE_ROOT:
if (!hasRoot()) {
if (!sDirectRoot) {
throw new Exception("Root is unavailable.");
}
// Disable server first
Expand Down Expand Up @@ -298,8 +316,8 @@ public static boolean hasRoot() {
@WorkerThread
@NoOps // Although we've used Ops checks, its overall usage does not affect anything
private static void autoDetectRootSystemOrAdbAndPersist(@NonNull Context context) {
sIsRoot = hasRoot();
if (sIsRoot) {
sIsRoot = sDirectRoot;
if (sDirectRoot) {
// Root permission was granted
setMode(MODE_ROOT);
// Disable remote server
Expand Down Expand Up @@ -388,7 +406,7 @@ private static void autoDetectRootSystemOrAdbAndPersist(@NonNull Context context
// Any message produced by the method below is just a helpful message.
checkRootOrIncompleteUsbDebuggingInAdb();
}
setMode(isPrivileged() ? MODE_ADB_OVER_TCP : MODE_NO_ROOT);
setMode(getWorkingUid() != Process.myUid() ? MODE_ADB_OVER_TCP : MODE_NO_ROOT);
}

@UiThread
Expand Down

0 comments on commit 04b8756

Please sign in to comment.