You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
259 lines
8.4 KiB
Java
259 lines
8.4 KiB
Java
package com.topjohnwu.magisk.superuser;
|
|
|
|
import android.content.ContentValues;
|
|
import android.content.Intent;
|
|
import android.content.pm.PackageManager;
|
|
import android.net.LocalSocket;
|
|
import android.net.LocalSocketAddress;
|
|
import android.os.Bundle;
|
|
import android.os.CountDownTimer;
|
|
import android.os.FileObserver;
|
|
import android.text.TextUtils;
|
|
import android.view.Window;
|
|
import android.widget.ArrayAdapter;
|
|
import android.widget.Button;
|
|
import android.widget.ImageView;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.Spinner;
|
|
import android.widget.TextView;
|
|
|
|
import com.topjohnwu.magisk.MagiskManager;
|
|
import com.topjohnwu.magisk.R;
|
|
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
|
import com.topjohnwu.magisk.components.Activity;
|
|
import com.topjohnwu.magisk.database.SuDatabaseHelper;
|
|
import com.topjohnwu.magisk.utils.CallbackEvent;
|
|
|
|
import java.io.DataInputStream;
|
|
import java.io.IOException;
|
|
|
|
import butterknife.BindView;
|
|
import butterknife.ButterKnife;
|
|
|
|
public class SuRequestActivity extends Activity implements CallbackEvent.Listener<Policy> {
|
|
|
|
private static final int[] timeoutList = {0, -1, 10, 20, 30, 60};
|
|
private static final int SU_PROTOCOL_PARAM_MAX = 20;
|
|
private static final int SU_PROTOCOL_NAME_MAX = 20;
|
|
private static final int SU_PROTOCOL_VALUE_MAX = 256;
|
|
|
|
private static final int PROMPT = 0;
|
|
private static final int AUTO_DENY = 1;
|
|
private static final int AUTO_ALLOW = 2;
|
|
|
|
@BindView(R.id.su_popup) LinearLayout suPopup;
|
|
@BindView(R.id.timeout) Spinner timeout;
|
|
@BindView(R.id.app_icon) ImageView appIcon;
|
|
@BindView(R.id.app_name) TextView appNameView;
|
|
@BindView(R.id.package_name) TextView packageNameView;
|
|
@BindView(R.id.grant_btn) Button grant_btn;
|
|
@BindView(R.id.deny_btn) Button deny_btn;
|
|
|
|
private String socketPath;
|
|
private LocalSocket socket;
|
|
private PackageManager pm;
|
|
private MagiskManager magiskManager;
|
|
|
|
private int uid;
|
|
private Policy policy;
|
|
private CountDownTimer timer;
|
|
private CallbackEvent.Listener<Policy> self;
|
|
private CallbackEvent<Policy> event = null;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
|
|
|
|
pm = getPackageManager();
|
|
magiskManager = getApplicationContext();
|
|
|
|
Intent intent = getIntent();
|
|
socketPath = intent.getStringExtra("socket");
|
|
self = this;
|
|
|
|
new FileObserver(socketPath) {
|
|
@Override
|
|
public void onEvent(int fileEvent, String path) {
|
|
if (fileEvent == FileObserver.DELETE_SELF) {
|
|
if (event != null)
|
|
event.trigger();
|
|
finish();
|
|
}
|
|
}
|
|
}.startWatching();
|
|
|
|
new SocketManager(this).exec();
|
|
}
|
|
|
|
void showRequest() {
|
|
|
|
switch (magiskManager.suResponseType) {
|
|
case AUTO_DENY:
|
|
handleAction(Policy.DENY, 0);
|
|
return;
|
|
case AUTO_ALLOW:
|
|
handleAction(Policy.ALLOW, 0);
|
|
return;
|
|
case PROMPT:
|
|
default:
|
|
}
|
|
|
|
setContentView(R.layout.activity_request);
|
|
ButterKnife.bind(this);
|
|
|
|
appIcon.setImageDrawable(policy.info.applicationInfo.loadIcon(pm));
|
|
appNameView.setText(policy.appName);
|
|
packageNameView.setText(policy.packageName);
|
|
|
|
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
|
|
R.array.allow_timeout, android.R.layout.simple_spinner_item);
|
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
timeout.setAdapter(adapter);
|
|
|
|
timer = new CountDownTimer(magiskManager.suRequestTimeout * 1000, 1000) {
|
|
@Override
|
|
public void onTick(long millisUntilFinished) {
|
|
deny_btn.setText(getString(R.string.deny_with_str, "(" + millisUntilFinished / 1000 + ")"));
|
|
}
|
|
@Override
|
|
public void onFinish() {
|
|
deny_btn.setText(getString(R.string.deny_with_str, "(0)"));
|
|
event.trigger();
|
|
}
|
|
};
|
|
|
|
grant_btn.setOnClickListener(v -> handleAction(Policy.ALLOW));
|
|
deny_btn.setOnClickListener(v -> handleAction(Policy.DENY));
|
|
suPopup.setOnClickListener((v) -> {
|
|
timer.cancel();
|
|
deny_btn.setText(getString(R.string.deny));
|
|
});
|
|
timeout.setOnTouchListener((v, event) -> {
|
|
timer.cancel();
|
|
deny_btn.setText(getString(R.string.deny));
|
|
return false;
|
|
});
|
|
|
|
timer.start();
|
|
}
|
|
|
|
@Override
|
|
public void onBackPressed() {
|
|
event.trigger();
|
|
}
|
|
|
|
@Override
|
|
public void onTrigger(CallbackEvent<Policy> event) {
|
|
Policy policy = event.getResult();
|
|
String response = "socket:DENY";
|
|
if (policy != null &&policy.policy == Policy.ALLOW )
|
|
response = "socket:ALLOW";
|
|
try {
|
|
socket.getOutputStream().write((response).getBytes());
|
|
} catch (Exception ignored) {}
|
|
finish();
|
|
}
|
|
|
|
void handleAction(int action) {
|
|
handleAction(action, timeoutList[timeout.getSelectedItemPosition()]);
|
|
}
|
|
|
|
void handleAction(int action, int time) {
|
|
policy.policy = action;
|
|
event.trigger(policy);
|
|
if (time >= 0) {
|
|
policy.until = time == 0 ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
|
|
new SuDatabaseHelper(this).addPolicy(policy);
|
|
}
|
|
}
|
|
|
|
private class SocketManager extends ParallelTask<Void, Void, Boolean> {
|
|
|
|
public SocketManager(Activity context) {
|
|
super(context);
|
|
}
|
|
|
|
@Override
|
|
protected Boolean doInBackground(Void... params) {
|
|
try{
|
|
socket = new LocalSocket();
|
|
socket.connect(new LocalSocketAddress(socketPath, LocalSocketAddress.Namespace.FILESYSTEM));
|
|
|
|
DataInputStream is = new DataInputStream(socket.getInputStream());
|
|
|
|
ContentValues payload = new ContentValues();
|
|
|
|
for (int i = 0; i < SU_PROTOCOL_PARAM_MAX; i++) {
|
|
|
|
int nameLen = is.readInt();
|
|
if (nameLen > SU_PROTOCOL_NAME_MAX)
|
|
throw new IllegalArgumentException("name length too long: " + nameLen);
|
|
|
|
byte[] nameBytes = new byte[nameLen];
|
|
is.readFully(nameBytes);
|
|
|
|
String name = new String(nameBytes);
|
|
|
|
if (TextUtils.equals(name, "eof"))
|
|
break;
|
|
|
|
int dataLen = is.readInt();
|
|
if (dataLen > SU_PROTOCOL_VALUE_MAX)
|
|
throw new IllegalArgumentException(name + " data length too long: " + dataLen);
|
|
|
|
byte[] dataBytes = new byte[dataLen];
|
|
is.readFully(dataBytes);
|
|
|
|
String data = new String(dataBytes);
|
|
|
|
payload.put(name, data);
|
|
}
|
|
|
|
if (payload.getAsInteger("uid") == null)
|
|
return false;
|
|
uid = payload.getAsInteger("uid");
|
|
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(Boolean result) {
|
|
if (!result) {
|
|
finish();
|
|
return;
|
|
}
|
|
boolean showRequest = false;
|
|
event = magiskManager.uidSuRequest.get(uid);
|
|
if (event == null) {
|
|
showRequest = true;
|
|
event = new CallbackEvent<Policy>() {
|
|
@Override
|
|
public void trigger(Policy result) {
|
|
super.trigger(result);
|
|
unRegister();
|
|
magiskManager.uidSuRequest.remove(uid);
|
|
}
|
|
};
|
|
magiskManager.uidSuRequest.put(uid, event);
|
|
}
|
|
event.register(self);
|
|
try {
|
|
if (showRequest) {
|
|
policy = new Policy(uid, pm);
|
|
showRequest();
|
|
} else {
|
|
finish();
|
|
}
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
e.printStackTrace();
|
|
event.trigger();
|
|
}
|
|
}
|
|
}
|
|
}
|